#
# This patch adds a few more interfaces to OpenSSL functions required for IPS.
# The additional interfaces are:
# pyOpenSSL -> OpenSSL
# crypto.CRL.verify() X509_CRL_verify()
# crypto.CRL.get_issuer() X509_CRL_get_issuer()
# crypto.CRL.get_next_update() X509_CRL_get_nextUpdate()
# crypto.X509.verify() X509_verify()
# crypto.X509.check_ca() X509_check_ca()
#
# The patch also adds test cases to the pyOpenSSL test suite for the added
# functions.
#
--- pyOpenSSL-0.13/OpenSSL/crypto/crl.c 2013-08-26 15:04:14.949389722 -0700
+++ pyOpenSSL-0.13/OpenSSL/crypto/crl.c 2013-08-26 15:05:00.183031221 -0700
@@ -180,6 +180,84 @@
return buffer;
}
+static char crypto_CRL_verify_doc[] = "\n\
+Verifies the CRL using the supplied public key\n\
+\n\
+@param key: a public key\n\
+@type key: L{PKey}\n\
+@return: True if the signature is correct.\n\
+@raise OpenSSL.crypto.Error: If the signature is invalid or there is a\n\
+ problem verifying the signature.\n\
+";
+
+PyObject *
+crypto_CRL_verify(crypto_CRLObj *self, PyObject *args)
+{
+ PyObject *obj;
+ crypto_PKeyObj *key;
+ int answer;
+
+ if (!PyArg_ParseTuple(args, "O!:verify", &crypto_PKey_Type, &obj)) {
+ return NULL;
+ }
+
+ key = (crypto_PKeyObj *)obj;
+
+ if ((answer = X509_CRL_verify(self->crl, key->pkey)) <= 0) {
+ exception_from_error_queue(crypto_Error);
+ return NULL;
+ }
+
+ return PyLong_FromLong(answer);
+}
+
+static char crypto_CRL_get_issuer_doc[] = "\n\
+Create an X509Name object for the issuer of the certificate\n\
+\n\
+@return: An X509Name object\n\
+";
+
+static PyObject *
+crypto_CRL_get_issuer(crypto_CRLObj *self, PyObject *args)
+{
+ crypto_X509NameObj *pyname;
+ X509_NAME *name;
+
+ if (!PyArg_ParseTuple(args, ":get_issuer"))
+ return NULL;
+
+ name = X509_CRL_get_issuer(self->crl);
+ pyname = crypto_X509Name_New(name, 0);
+ if (pyname != NULL)
+ {
+ pyname->parent_cert = (PyObject *)self;
+ Py_INCREF(self);
+ }
+ return (PyObject *)pyname;
+}
+
+static char crypto_CRL_get_nextUpdate_doc[] = "\n\
+Retrieve the time stamp for when the CRL gets its next update\n\
+\n\
+@return: A string giving the timestamp, in the format:\n\
+\n\
+ YYYYMMDDhhmmssZ\n\
+ YYYYMMDDhhmmss+hhmm\n\
+ YYYYMMDDhhmmss-hhmm\n\
+ or None if there is no value set.\n\
+";
+
+static PyObject*
+crypto_CRL_get_nextUpdate(crypto_CRLObj *self, PyObject *args)
+{
+ /*
+ * X509_CRL_get_nextUpdate returns a borrowed reference.
+ */
+ return _get_asn1_time(
+ ":get_nextUpdate", X509_CRL_get_nextUpdate(self->crl), args);
+}
+
+
crypto_CRLObj *
crypto_CRL_New(X509_CRL *crl) {
crypto_CRLObj *self;
@@ -205,6 +283,9 @@
ADD_KW_METHOD(add_revoked),
ADD_METHOD(get_revoked),
ADD_KW_METHOD(export),
+ ADD_KW_METHOD(verify),
+ ADD_KW_METHOD(get_issuer),
+ ADD_KW_METHOD(get_nextUpdate),
{ NULL, NULL }
};
#undef ADD_METHOD
--- pyOpenSSL-0.13/OpenSSL/crypto/x509.c 2013-08-26 15:04:14.943271276 -0700
+++ pyOpenSSL-0.13/OpenSSL/crypto/x509.c 2013-08-26 15:05:00.183501160 -0700
@@ -761,6 +761,57 @@
return (PyObject*)extobj;
}
+static char crypto_X509_verify_doc[] = "\n\
+Verifies the certificate using the supplied public key\n\
+\n\
+@param key: a public key\n\
+@type key: L{PKey}\n\
+@return: True if the signature is correct.\n\
+@raise OpenSSL.crypto.Error: If the signature is invalid or there is a\n\
+ problem verifying the signature.\n\
+";
+
+PyObject *
+crypto_X509_verify(crypto_X509Obj *self, PyObject *args)
+{
+ PyObject *obj;
+ crypto_PKeyObj *key;
+ int answer;
+
+ if (!PyArg_ParseTuple(args, "O!:verify", &crypto_PKey_Type, &obj)) {
+ return NULL;
+ }
+
+ key = (crypto_PKeyObj *)obj;
+
+ if ((answer = X509_verify(self->x509, key->pkey)) <= 0) {
+ exception_from_error_queue(crypto_Error);
+ return NULL;
+ }
+
+ return PyLong_FromLong(answer);
+}
+
+
+static char crypto_X509_check_ca_doc[] = "\n\
+Checks if the certificate is a CA\n\
+\n\
+@return: 0 if not a CA, >0 if a CA\n\
+";
+
+PyObject *
+crypto_X509_check_ca(crypto_X509Obj *self, PyObject *args)
+{
+ int answer;
+
+ if (!PyArg_ParseTuple(args, ":check_ca"))
+ return NULL;
+
+ answer = X509_check_ca(self->x509);
+
+ return PyLong_FromLong(answer);
+}
+
/*
* ADD_METHOD(name) expands to a correct PyMethodDef declaration
* { 'name', (PyCFunction)crypto_X509_name, METH_VARARGS }
@@ -794,6 +845,8 @@
ADD_METHOD(add_extensions),
ADD_METHOD(get_extension),
ADD_METHOD(get_extension_count),
+ ADD_METHOD(verify),
+ ADD_METHOD(check_ca),
{ NULL, NULL }
};
#undef ADD_METHOD
--- pyOpenSSL-0.13/OpenSSL/test/test_crypto.py 2013-08-26 15:04:14.951459483 -0700
+++ pyOpenSSL-0.13/OpenSSL/test/test_crypto.py 2013-08-26 15:14:40.335995703 -0700
@@ -1090,6 +1090,18 @@
WpOdIpB8KksUTCzV591Nr1wd
-----END CERTIFICATE-----
"""
+ def setUp(self):
+ # create new CA
+ self.ca_key = PKey()
+ self.ca_key.generate_key(TYPE_RSA, 384)
+
+ self.ca = X509()
+ self.ca.get_subject().commonName = "Yoda root CA"
+ self.ca.set_issuer(self.ca.get_subject())
+ self.ca.set_pubkey(self.ca_key)
+ self.ca.sign(self.ca_key, "sha1")
+
+
def signable(self):
"""
Create and return a new L{X509}.
@@ -1620,6 +1632,51 @@
self.assertRaises(ValueError, cert.get_signature_algorithm)
+ def test_key_verify(self):
+ """
+ L{X509.verify} succeeds when passed a valid CA key, raises
+ L{OpenSSL.crypto.Error} otherwise.
+ """
+ key = PKey()
+ key.generate_key(TYPE_RSA, 384)
+ req = X509Req()
+ req.get_subject().commonName = "Master Luke"
+ req.set_pubkey(key)
+ req.sign(key, "sha1")
+ cert = X509()
+ cert.set_subject(req.get_subject())
+ cert.set_pubkey(key)
+ cert.set_issuer(self.ca.get_subject())
+ cert.sign(self.ca_key, "sha1")
+
+ self.assertTrue(cert.verify(self.ca_key))
+ self.assertRaises(Error, cert.verify, key)
+
+
+ def test_is_ca(self):
+ """
+ L{X509.check_ca} returns a value >0 if certificate is a CA, returns 0
+ if not.
+ """
+ res = self.ca.check_ca()
+ self.assertTrue(res > 0)
+
+ # Try with a non-ca cert
+ key = PKey()
+ key.generate_key(TYPE_RSA, 384)
+ req = X509Req()
+ req.get_subject().commonName = "Master Luke"
+ req.set_pubkey(key)
+ req.sign(key, "sha1")
+ cert = X509()
+ cert.set_subject(req.get_subject())
+ cert.set_pubkey(key)
+ cert.set_issuer(self.ca.get_subject())
+ cert.sign(self.ca_key, "sha1")
+ res = cert.check_ca()
+ self.assertEqual(res, 0)
+
+
class PKCS12Tests(TestCase):
"""
@@ -2521,6 +2578,18 @@
cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ def setUp(self):
+ # create new CA
+ self.ca_key = PKey()
+ self.ca_key.generate_key(TYPE_RSA, 384)
+
+ self.ca = X509()
+ self.ca.get_subject().commonName = "Yoda root CA"
+ self.ca.set_issuer(self.ca.get_subject())
+ self.ca.set_pubkey(self.ca_key)
+ self.ca.sign(self.ca_key, "sha1")
+
+
def test_construction(self):
"""
Confirm we can create L{OpenSSL.crypto.CRL}. Check
@@ -2712,6 +2781,44 @@
self.assertRaises(Error, load_crl, FILETYPE_PEM, "hello, world")
+ def test_crl_verify(self):
+ """
+ Test that L{OpenSSL.CRL.verify} correctly verifies CRL with the
+ pubkey of the issuing CA, raises L{OpenSSL.crypto.Error} in case of
+ bogus key.
+ """
+ s = CRL().export(self.ca, self.ca_key)
+ crl = load_crl(FILETYPE_PEM, s)
+ res = crl.verify(self.ca_key)
+ self.assertTrue(res)
+
+ boguskey = PKey()
+ boguskey.generate_key(TYPE_RSA, 384)
+ self.assertRaises(Error, crl.verify, boguskey)
+
+
+ def test_crl_get_issuer(self):
+ """
+ Test that L{OpenSSL.CRL.get_issuer} returns a L{OpenSSL.X509Name} object
+ with the correct issuer information.
+ """
+ s = CRL().export(self.ca, self.ca_key)
+ crl = load_crl(FILETYPE_PEM, s)
+ issuer = crl.get_issuer()
+ self.assertTrue(isinstance(issuer, X509Name))
+ self.assertTrue(issuer.commonName == self.ca.get_subject().commonName)
+
+
+ def test_crl_get_nextUpdate(self):
+ """
+ Test that L{OpenSSL.CRL.get_nextUpdate} returns the correct date and
+ time of next update.
+ """
+ crl = load_crl(FILETYPE_PEM, crlData)
+ self.assertEqual(crl.get_nextUpdate(), "20120927024152Z")
+
+
+
class SignVerifyTests(TestCase):
"""
Tests for L{OpenSSL.crypto.sign} and L{OpenSSL.crypto.verify}.