merge trunk
diff --git a/doc/pyOpenSSL.tex b/doc/pyOpenSSL.tex
index d772c25..a41b575 100644
--- a/doc/pyOpenSSL.tex
+++ b/doc/pyOpenSSL.tex
@@ -207,6 +207,14 @@
method.
\end{classdesc}
+\begin{classdesc}{CRL}{}
+A class representing Certifcate Revocation List objects.
+\end{classdesc}
+
+\begin{classdesc}{Revoked}{}
+A class representing Revocation objects of CRL.
+\end{classdesc}
+
\begin{datadesc}{FILETYPE_PEM}
\dataline{FILETYPE_ASN1}
File type constants.
@@ -259,6 +267,12 @@
pass phrase.
\end{funcdesc}
+\begin{funcdesc}{load_crl}{type, buffer}
+Load Certificate Revocation List (CRL) data from a string \var{buffer}.
+\var{buffer} encoded with the type \var{type}. The type \var{type}
+must either \constant{FILETYPE_PEM} or \constant{FILETYPE_ASN1}).
+\end{funcdesc}
+
\begin{funcdesc}{load_pkcs7_data}{type, buffer}
Load pkcs7 data from the string \var{buffer} encoded with the type \var{type}.
\end{funcdesc}
@@ -612,6 +626,60 @@
Verify the NetscapeSPKI object using the given \var{key}.
\end{methoddesc}
+\subsubsection{CRL objects \label{crl}}
+
+CRL objects have the following methods:
+
+\begin{methoddesc}[CRL]{add_revoked}{revoked}
+Add a Revoked object to the CRL, by value not reference.
+\end{methoddesc}
+
+\begin{methoddesc}[CRL]{export}{cert, key\optional{, type=FILETYPE_PEM}\optional{, days=100}}
+Use \var{cert} and \var{key} to sign the CRL and return the CRL as a string.
+\var{days} is the number of days before the next CRL is due.
+\end{methoddesc}
+
+\begin{methoddesc}[CRL]{get_revoked}{}
+Return a tuple of Revoked objects, by value not reference.
+\end{methoddesc}
+
+\subsubsection{Revoked objects \label{revoked}}
+
+Revoked objects have the following methods:
+
+\begin{methoddesc}[Revoked]{all_reasons}{}
+Return a list of all supported reasons.
+\end{methoddesc}
+
+\begin{methoddesc}[Revoked]{get_reason}{}
+Return the revocation reason as a str. Can be
+None, which differs from "Unspecified".
+\end{methoddesc}
+
+\begin{methoddesc}[Revoked]{get_rev_date}{}
+Return the revocation date as a str.
+The string is formatted as an ASN1 GENERALIZEDTIME.
+\end{methoddesc}
+
+\begin{methoddesc}[Revoked]{get_serial}{}
+Return a str containing a hex number of the serial of the revoked certificate.
+\end{methoddesc}
+
+\begin{methoddesc}[Revoked]{set_reason}{reason}
+Set the revocation reason. \var{reason} must
+be None or a string, but the values are limited.
+Spaces and case are ignored. See \method{all_reasons}.
+\end{methoddesc}
+
+\begin{methoddesc}[Revoked]{set_rev_date}{date}
+Set the revocation date.
+The string is formatted as an ASN1 GENERALIZEDTIME.
+\end{methoddesc}
+
+\begin{methoddesc}[Revoked]{set_serial}{serial}
+\var{serial} is a string containing a hex number of the serial of the revoked certificate.
+\end{methoddesc}
+
% % % rand module
diff --git a/setup.py b/setup.py
index af02aae..9cb4364 100755
--- a/setup.py
+++ b/setup.py
@@ -26,12 +26,14 @@
'src/crypto/x509store.c', 'src/crypto/x509req.c',
'src/crypto/x509ext.c', 'src/crypto/pkcs7.c',
'src/crypto/pkcs12.c', 'src/crypto/netscape_spki.c',
+ 'src/crypto/revoked.c', 'src/crypto/crl.c',
'src/util.c']
crypto_dep = ['src/crypto/crypto.h', 'src/crypto/x509.h',
'src/crypto/x509name.h', 'src/crypto/pkey.h',
'src/crypto/x509store.h', 'src/crypto/x509req.h',
'src/crypto/x509ext.h', 'src/crypto/pkcs7.h',
'src/crypto/pkcs12.h', 'src/crypto/netscape_spki.h',
+ 'src/crypto/revoked.h', 'src/crypto/crl.h',
'src/util.h']
rand_src = ['src/rand/rand.c', 'src/util.c']
rand_dep = ['src/util.h']
diff --git a/src/crypto/crl.c b/src/crypto/crl.c
new file mode 100644
index 0000000..8e1c135
--- /dev/null
+++ b/src/crypto/crl.c
@@ -0,0 +1,290 @@
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+
+static X509_REVOKED * X509_REVOKED_dup(X509_REVOKED *orig) {
+ X509_REVOKED *dupe = NULL;
+
+ dupe = X509_REVOKED_new();
+ if (dupe == NULL) {
+ return NULL;
+ }
+ if (orig->serialNumber) {
+ dupe->serialNumber = M_ASN1_INTEGER_dup(orig->serialNumber);
+ }
+ if (orig->revocationDate) {
+ dupe->revocationDate = M_ASN1_INTEGER_dup(orig->revocationDate);
+ }
+ if (orig->extensions) {
+ STACK_OF(X509_EXTENSION) *sk = NULL;
+ X509_EXTENSION * ext;
+ int j;
+
+ sk = sk_X509_EXTENSION_new_null();
+ for (j = 0; j < sk_X509_EXTENSION_num(orig->extensions); j++) {
+ ext = sk_X509_EXTENSION_value(orig->extensions, j);
+ ext = X509_EXTENSION_dup(ext);
+ sk_X509_EXTENSION_push(sk, ext);
+ }
+ dupe->extensions = sk;
+ }
+ dupe->sequence = orig->sequence;
+ return dupe;
+}
+
+static char crypto_CRL_get_revoked_doc[] = "\n\
+Return revoked portion of the CRL structure (by value\n\
+not reference).\n\
+\n\
+@return: A tuple of Revoked objects.\n\
+";
+static PyObject *
+crypto_CRL_get_revoked(crypto_CRLObj *self, PyObject *args) {
+ int j, num_rev;
+ X509_REVOKED *r = NULL;
+ PyObject *obj = NULL, *rev_obj;
+
+ if (!PyArg_ParseTuple(args, ":get_revoked")) {
+ return NULL;
+ }
+
+ num_rev = sk_X509_REVOKED_num(self->crl->crl->revoked);
+ if (num_rev < 0) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ if ((obj = PyTuple_New(num_rev)) == NULL) {
+ return NULL;
+ }
+
+ for (j = 0; j < num_rev; j++) {
+ r = sk_X509_REVOKED_value(self->crl->crl->revoked, j);
+ r = X509_REVOKED_dup(r);
+ if (r == NULL ) {
+ goto error;
+ }
+ rev_obj = (PyObject *) crypto_Revoked_New(r);
+ if (rev_obj == NULL) {
+ goto error;
+ }
+ r = NULL; /* it's now owned by rev_obj */
+ PyTuple_SET_ITEM(obj, j, rev_obj);
+ }
+ return obj;
+
+ error:
+ if (r) {
+ X509_REVOKED_free(r);
+ }
+ Py_XDECREF(obj);
+ return NULL;
+}
+
+static char crypto_CRL_add_revoked_doc[] = "\n\
+Add a revoked (by value not reference) to the CRL structure\n\
+\n\
+@param cert: The new revoked.\n\
+@type cert: L{X509}\n\
+@return: None\n\
+";
+static PyObject *
+crypto_CRL_add_revoked(crypto_CRLObj *self, PyObject *args, PyObject *keywds) {
+ crypto_RevokedObj * rev_obj = NULL;
+ static char *kwlist[] = {"revoked", NULL};
+ X509_REVOKED * dup;
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!:add_revoked",
+ kwlist, &crypto_Revoked_Type, &rev_obj)) {
+ return NULL;
+ }
+
+ dup = X509_REVOKED_dup( rev_obj->revoked );
+ if (dup == NULL) {
+ return NULL;
+ }
+ X509_CRL_add0_revoked(self->crl, dup);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static char crypto_CRL_export_doc[] = "\n\
+export(cert, key[, type[, days]]) -> export a CRL as a string\n\
+\n\
+@param cert: Used to sign CRL.\n\
+@type cert: L{X509}\n\
+@param key: Used to sign CRL.\n\
+@type key: L{PKey}\n\
+@return: None\n\
+";
+static PyObject *
+crypto_CRL_export(crypto_CRLObj *self, PyObject *args, PyObject *keywds) {
+ int ret, buf_len, type = X509_FILETYPE_PEM, days = 100;
+ char *temp;
+ BIO *bio;
+ PyObject *buffer;
+ crypto_PKeyObj *key;
+ ASN1_TIME *tmptm;
+ crypto_X509Obj *x509;
+ static char *kwlist[] = {"cert", "key", "type", "days", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!O!|ii:dump_crl", kwlist,
+ &crypto_X509_Type, &x509,
+ &crypto_PKey_Type, &key, &type, &days)) {
+ return NULL;
+ }
+
+ bio = BIO_new(BIO_s_mem());
+ tmptm = ASN1_TIME_new();
+ if (!tmptm) {
+ return 0;
+ }
+ X509_gmtime_adj(tmptm,0);
+ X509_CRL_set_lastUpdate(self->crl, tmptm);
+ X509_gmtime_adj(tmptm,days*24*60*60);
+ X509_CRL_set_nextUpdate(self->crl, tmptm);
+ ASN1_TIME_free(tmptm);
+ X509_CRL_set_issuer_name(self->crl, X509_get_subject_name(x509->x509));
+ X509_CRL_sign(self->crl, key->pkey, EVP_md5());
+ switch (type) {
+ case X509_FILETYPE_PEM:
+ ret = PEM_write_bio_X509_CRL(bio, self->crl);
+ break;
+
+ case X509_FILETYPE_ASN1:
+ ret = (int) i2d_X509_CRL_bio(bio, self->crl);
+ break;
+
+ case X509_FILETYPE_TEXT:
+ ret = X509_CRL_print(bio, self->crl);
+ break;
+
+ default:
+ PyErr_SetString(
+ PyExc_ValueError,
+ "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT");
+ return NULL;
+ }
+ if (!ret) {
+ exception_from_error_queue(crypto_Error);
+ BIO_free(bio);
+ return NULL;
+ }
+ buf_len = BIO_get_mem_data(bio, &temp);
+ buffer = PyString_FromStringAndSize(temp, buf_len);
+ BIO_free(bio);
+ return buffer;
+}
+
+crypto_CRLObj *
+crypto_CRL_New(X509_CRL *crl) {
+ crypto_CRLObj *self;
+
+ self = PyObject_New(crypto_CRLObj, &crypto_CRL_Type);
+ if (self == NULL) {
+ return NULL;
+ }
+ self->crl = crl;
+ return self;
+}
+
+/*
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ * { 'name', (PyCFunction)crypto_CRL_name, METH_VARARGS, crypto_CRL_name_doc }
+ * for convenience
+ */
+#define ADD_METHOD(name) \
+ { #name, (PyCFunction)crypto_CRL_##name, METH_VARARGS, crypto_CRL_##name##_doc }
+#define ADD_KW_METHOD(name) \
+ { #name, (PyCFunction)crypto_CRL_##name, METH_VARARGS | METH_KEYWORDS, crypto_CRL_##name##_doc }
+static PyMethodDef crypto_CRL_methods[] = {
+ ADD_KW_METHOD(add_revoked),
+ ADD_METHOD(get_revoked),
+ ADD_KW_METHOD(export),
+ { NULL, NULL }
+};
+#undef ADD_METHOD
+
+
+static PyObject *
+crypto_CRL_getattr(crypto_CRLObj *self, char *name) {
+ return Py_FindMethod(crypto_CRL_methods, (PyObject *)self, name);
+}
+
+static void
+crypto_CRL_dealloc(crypto_CRLObj *self) {
+ X509_CRL_free(self->crl);
+ self->crl = NULL;
+
+ PyObject_Del(self);
+}
+
+static char crypto_CRL_doc[] = "\n\
+CRL() -> CRL instance\n\
+\n\
+Create a new empty CRL object.\n\
+\n\
+@returns: The CRL object\n\
+";
+
+static PyObject* crypto_CRL_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) {
+ if (!PyArg_ParseTuple(args, ":CRL")) {
+ return NULL;
+ }
+
+ return (PyObject *)crypto_CRL_New(X509_CRL_new());
+}
+
+PyTypeObject crypto_CRL_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "CRL",
+ sizeof(crypto_CRLObj),
+ 0,
+ (destructor)crypto_CRL_dealloc,
+ NULL, /* print */
+ (getattrfunc)crypto_CRL_getattr,
+ NULL, /* setattr */
+ NULL, /* compare */
+ NULL, /* repr */
+ NULL, /* as_number */
+ NULL, /* as_sequence */
+ NULL, /* as_mapping */
+ NULL, /* hash */
+ NULL, /* call */
+ NULL, /* str */
+ NULL, /* getattro */
+ NULL, /* setattro */
+ NULL, /* as_buffer */
+ Py_TPFLAGS_DEFAULT,
+ crypto_CRL_doc, /* doc */
+ NULL, /* traverse */
+ NULL, /* clear */
+ NULL, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ NULL, /* tp_iter */
+ NULL, /* tp_iternext */
+ crypto_CRL_methods, /* tp_methods */
+ NULL, /* tp_members */
+ NULL, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ NULL, /* tp_descr_get */
+ NULL, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ NULL, /* tp_init */
+ NULL, /* tp_alloc */
+ crypto_CRL_new, /* tp_new */
+};
+
+int init_crypto_crl(PyObject *module) {
+ if (PyType_Ready(&crypto_CRL_Type) < 0) {
+ return 0;
+ }
+
+ if (PyModule_AddObject(module, "CRL", (PyObject *)&crypto_CRL_Type) != 0) {
+ return 0;
+ }
+ return 1;
+}
diff --git a/src/crypto/crl.h b/src/crypto/crl.h
new file mode 100644
index 0000000..87f5048
--- /dev/null
+++ b/src/crypto/crl.h
@@ -0,0 +1,19 @@
+#ifndef PyOpenSSL_crypto_CRL_H_
+#define PyOpenSSL_crypto_CRL_H_
+
+#include <Python.h>
+
+extern int init_crypto_crl (PyObject *);
+
+extern PyTypeObject crypto_CRL_Type;
+
+#define crypto_CRL_Check(v) ((v)->ob_type == &crypto_CRL_Type)
+
+typedef struct {
+ PyObject_HEAD
+ X509_CRL *crl;
+} crypto_CRLObj;
+
+crypto_CRLObj * crypto_CRL_New(X509_CRL *crl);
+
+#endif
diff --git a/src/crypto/crypto.c b/src/crypto/crypto.c
index dd279cf..df48a65 100644
--- a/src/crypto/crypto.c
+++ b/src/crypto/crypto.c
@@ -432,6 +432,51 @@
return buffer;
}
+static char crypto_load_crl_doc[] = "\n\
+Load a certificate revocation list from a buffer\n\
+\n\
+@param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\
+@param buffer: The buffer the CRL is stored in\n\
+\n\
+@return: The PKey object\n\
+";
+
+static PyObject *
+crypto_load_crl(PyObject *spam, PyObject *args) {
+ int type, len;
+ char *buffer;
+ BIO *bio;
+ X509_CRL *crl;
+
+ if (!PyArg_ParseTuple(args, "is#:load_crl", &type, &buffer, &len)) {
+ return NULL;
+ }
+
+ bio = BIO_new_mem_buf(buffer, len);
+ switch (type) {
+ case X509_FILETYPE_PEM:
+ crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL);
+ break;
+
+ case X509_FILETYPE_ASN1:
+ crl = d2i_X509_CRL_bio(bio, NULL);
+ break;
+
+ default:
+ PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
+ BIO_free(bio);
+ return NULL;
+ }
+ BIO_free(bio);
+
+ if (crl == NULL) {
+ exception_from_error_queue(crypto_Error);
+ return NULL;
+ }
+
+ return (PyObject *)crypto_CRL_New(crl);
+}
+
static char crypto_load_pkcs7_data_doc[] = "\n\
Load pkcs7 data from a buffer\n\
\n\
@@ -651,6 +696,7 @@
{ "dump_certificate", (PyCFunction)crypto_dump_certificate, METH_VARARGS, crypto_dump_certificate_doc },
{ "load_certificate_request", (PyCFunction)crypto_load_certificate_request, METH_VARARGS, crypto_load_certificate_request_doc },
{ "dump_certificate_request", (PyCFunction)crypto_dump_certificate_request, METH_VARARGS, crypto_dump_certificate_request_doc },
+ { "load_crl", (PyCFunction)crypto_load_crl, METH_VARARGS, crypto_load_crl_doc },
{ "load_pkcs7_data", (PyCFunction)crypto_load_pkcs7_data, METH_VARARGS, crypto_load_pkcs7_data_doc },
{ "load_pkcs12", (PyCFunction)crypto_load_pkcs12, METH_VARARGS, crypto_load_pkcs12_doc },
{ "sign", (PyCFunction)crypto_sign, METH_VARARGS, crypto_sign_doc },
@@ -794,7 +840,10 @@
goto error;
if (!init_crypto_netscape_spki(module))
goto error;
-
+ if (!init_crypto_crl(module))
+ goto error;
+ if (!init_crypto_revoked(module))
+ goto error;
error:
;
}
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index b5e6b65..057b5dd 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -23,6 +23,8 @@
#include "x509ext.h"
#include "pkcs7.h"
#include "pkcs12.h"
+#include "crl.h"
+#include "revoked.h"
#include "../util.h"
extern PyObject *crypto_Error;
diff --git a/src/crypto/revoked.c b/src/crypto/revoked.c
new file mode 100644
index 0000000..3042c4d
--- /dev/null
+++ b/src/crypto/revoked.c
@@ -0,0 +1,445 @@
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+#ifdef _WIN32
+#define strcasecmp(string1, string2) _stricmp(string1, string2)
+#endif
+
+/* http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_ */
+/* which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches */
+/* OCSP_crl_reason_str. We use the latter, just like the command line program. */
+static const char *crl_reasons[] = {
+ "unspecified",
+ "keyCompromise",
+ "CACompromise",
+ "affiliationChanged",
+ "superseded",
+ "cessationOfOperation",
+ "certificateHold",
+ NULL,
+ "removeFromCRL",
+};
+
+#define NUM_REASONS (sizeof(crl_reasons) / sizeof(char *))
+
+static char crypto_Revoked_all_reasons_doc[] = "\n\
+Return a list of all the supported reason strings.\n\
+\n\
+@return: A list of reason strings.\n\
+";
+static PyObject *
+crypto_Revoked_all_reasons(crypto_RevokedObj *self, PyObject *args) {
+ PyObject *list, *str;
+ int j;
+
+ list = PyList_New(0);
+ for (j = 0; j < NUM_REASONS; j++) {
+ if(crl_reasons[j]) {
+ str = PyString_FromString(crl_reasons[j]);
+ PyList_Append(list, str);
+ Py_DECREF(str);
+ }
+ }
+ return list;
+}
+
+static PyObject *
+X509_EXTENSION_value_to_PyString(X509_EXTENSION *ex) {
+ BIO *bio = NULL;
+ PyObject *str = NULL;
+ int str_len;
+ char *tmp_str;
+
+ /* Create a openssl BIO buffer */
+ bio = BIO_new(BIO_s_mem());
+ if (bio == NULL) {
+ goto err;
+ }
+
+ /* These are not the droids you are looking for. */
+ if (!X509V3_EXT_print(bio, ex, 0, 0)) {
+ if (M_ASN1_OCTET_STRING_print(bio, ex->value) == 0) {
+ goto err;
+ }
+ }
+
+ /* Convert to a Python string. */
+ str_len = BIO_get_mem_data(bio, &tmp_str);
+ str = PyString_FromStringAndSize(tmp_str, str_len);
+
+ /* Cleanup */
+ BIO_free(bio);
+ return str;
+
+ err:
+ if (bio) {
+ BIO_free(bio);
+ }
+ if (str) {
+ Py_DECREF(str);
+ }
+ return NULL;
+}
+
+static void
+delete_reason(STACK_OF(X509_EXTENSION) *sk) {
+ X509_EXTENSION * ext;
+ int j;
+
+ for (j = 0; j < sk_X509_EXTENSION_num(sk); j++) {
+ ext = sk_X509_EXTENSION_value(sk, j);
+ if (OBJ_obj2nid(ext->object) == NID_crl_reason) {
+ X509_EXTENSION_free(ext);
+ (void) sk_X509_EXTENSION_delete(sk, j);
+ break;
+ }
+ }
+}
+
+static int
+reason_str_to_code(const char * reason_str) {
+ int reason_code = -1, j;
+ char *spaceless_reason, * sp;
+
+ /* Remove spaces so that the responses of
+ * get_reason() work in set_reason() */
+ if ((spaceless_reason = strdup(reason_str)) == NULL) {
+ return -1;
+ }
+
+ while ((sp = strchr(spaceless_reason, ' '))) {
+ memmove(sp, sp+1, strlen(sp));
+ }
+
+ for (j = 0; j < NUM_REASONS; j++) {
+ if(crl_reasons[j] && !strcasecmp(spaceless_reason, crl_reasons[j])) {
+ reason_code = j;
+ break;
+ }
+ }
+ free(spaceless_reason);
+ return reason_code;
+}
+
+static char crypto_Revoked_set_reason_doc[] = "\n\
+Set the reason of a Revoked object.\n\
+\n\
+@param reason: The reason string.\n\
+@type reason: L{str}\n\
+@return: None\n\
+";
+static PyObject *
+crypto_Revoked_set_reason(crypto_RevokedObj *self, PyObject *args, PyObject *keywds) {
+ static char *kwlist[] = {"reason", NULL};
+ const char *reason_str = NULL;
+ int reason_code;
+ ASN1_ENUMERATED *rtmp = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "z:set_reason",
+ kwlist, &reason_str)) {
+ return NULL;
+ }
+
+ if(reason_str == NULL) {
+ delete_reason(self->revoked->extensions);
+ goto done;
+ }
+
+ reason_code = reason_str_to_code(reason_str);
+ if (reason_code == -1) {
+ PyErr_SetString(PyExc_ValueError, "bad reason string");
+ return NULL;
+ }
+
+ rtmp = ASN1_ENUMERATED_new();
+ if (!rtmp || !ASN1_ENUMERATED_set(rtmp, reason_code)) {
+ goto err;
+ }
+ delete_reason(self->revoked->extensions);
+ if (!X509_REVOKED_add1_ext_i2d(self->revoked, NID_crl_reason, rtmp, 0, 0)) {
+ goto err;
+ }
+
+ done:
+ Py_INCREF(Py_None);
+ return Py_None;
+
+ err:
+ exception_from_error_queue(crypto_Error);
+ return NULL;
+}
+
+
+static char crypto_Revoked_get_reason_doc[] = "\n\
+Return the reason of a Revoked object.\n\
+\n\
+@return: The reason as a string\n\
+";
+static PyObject *
+crypto_Revoked_get_reason(crypto_RevokedObj *self, PyObject *args) {
+ X509_EXTENSION * ext;
+ int j;
+ STACK_OF(X509_EXTENSION) *sk = NULL;
+
+ if (!PyArg_ParseTuple(args, ":get_reason")) {
+ return NULL;
+ }
+
+ sk = self->revoked->extensions;
+ for (j = 0; j < sk_X509_EXTENSION_num(sk); j++) {
+ ext = sk_X509_EXTENSION_value(sk, j);
+ if (OBJ_obj2nid(ext->object) == NID_crl_reason) {
+ return X509_EXTENSION_value_to_PyString(ext);
+ }
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static char crypto_Revoked_get_rev_date_doc[] = "\n\
+Retrieve the revocation date\n\
+\n\
+@return: A string giving the timestamp, in the format:\n\
+\n\
+ YYYYMMDDhhmmssZ\n\
+ YYYYMMDDhhmmss+hhmm\n\
+ YYYYMMDDhhmmss-hhmm\n\
+";
+
+static PyObject*
+crypto_Revoked_get_rev_date(crypto_RevokedObj *self, PyObject *args) {
+ /* returns a borrowed reference. */
+ return _get_asn1_time(
+ ":get_rev_date", self->revoked->revocationDate, args);
+}
+
+static char crypto_Revoked_set_rev_date_doc[] = "\n\
+Set the revocation timestamp\n\
+\n\
+@param when: A string giving the timestamp, in the format:\n\
+\n\
+ YYYYMMDDhhmmssZ\n\
+ YYYYMMDDhhmmss+hhmm\n\
+ YYYYMMDDhhmmss-hhmm\n\
+\n\
+@return: None\n\
+";
+
+static PyObject*
+crypto_Revoked_set_rev_date(crypto_RevokedObj *self, PyObject *args) {
+ return _set_asn1_time(
+ "s:set_rev_date", self->revoked->revocationDate, args);
+}
+
+/* The integer is converted to an upper-case hex string
+ * without a '0x' prefix. */
+static PyObject *
+ASN1_INTEGER_to_PyString(ASN1_INTEGER *asn1_int) {
+ BIO *bio = NULL;
+ PyObject *str = NULL;
+ int str_len;
+ char *tmp_str;
+
+ /* Create a openssl BIO buffer */
+ bio = BIO_new(BIO_s_mem());
+ if (bio == NULL) {
+ goto err;
+ }
+
+ /* Write the integer to the BIO as a hex string. */
+ if (i2a_ASN1_INTEGER(bio, asn1_int) < 0) {
+ goto err;
+ }
+
+ /* Convert to a Python string. */
+ str_len = BIO_get_mem_data(bio, &tmp_str);
+ str = PyString_FromStringAndSize(tmp_str, str_len);
+
+ /* Cleanup */
+ BIO_free(bio);
+ return str;
+
+ err:
+ if (bio) {
+ BIO_free(bio);
+ }
+ if (str) {
+ Py_DECREF(str);
+ }
+ return NULL;
+}
+
+
+static char crypto_Revoked_get_serial_doc[] = "\n\
+Return the serial number of a Revoked structure\n\
+\n\
+@return: The serial number as a string\n\
+";
+static PyObject *
+crypto_Revoked_get_serial(crypto_RevokedObj *self, PyObject *args) {
+ if (!PyArg_ParseTuple(args, ":get_serial")) {
+ return NULL;
+ }
+
+ if (self->revoked->serialNumber == NULL) {
+ /* never happens */
+ Py_INCREF(Py_None);
+ return Py_None;
+ } else {
+ return ASN1_INTEGER_to_PyString(self->revoked->serialNumber);
+ }
+}
+
+static char crypto_Revoked_set_serial_doc[] = "\n\
+Set the serial number of a revoked Revoked structure\n\
+\n\
+@param hex_str: The new serial number.\n\
+@type hex_str: L{str}\n\
+@return: None\n\
+";
+static PyObject *
+crypto_Revoked_set_serial(crypto_RevokedObj *self, PyObject *args, PyObject *keywds) {
+ static char *kwlist[] = {"hex_str", NULL};
+ const char *hex_str = NULL;
+ BIGNUM *serial = NULL;
+ ASN1_INTEGER *tmpser = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "s:set_serial",
+ kwlist, &hex_str)) {
+ return NULL;
+ }
+
+ if (!BN_hex2bn(&serial, hex_str) ) {
+ PyErr_SetString(PyExc_ValueError, "bad hex string");
+ return NULL;
+ }
+
+ tmpser = BN_to_ASN1_INTEGER(serial, NULL);
+ BN_free(serial);
+ serial = NULL;
+ X509_REVOKED_set_serialNumber(self->revoked, tmpser);
+ ASN1_INTEGER_free(tmpser);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+crypto_RevokedObj *
+crypto_Revoked_New(X509_REVOKED *revoked) {
+ crypto_RevokedObj *self;
+
+ self = PyObject_New(crypto_RevokedObj, &crypto_Revoked_Type);
+ if (self == NULL) {
+ return NULL;
+ }
+ self->revoked = revoked;
+ return self;
+}
+
+/*
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ * { 'name', (PyCFunction)crypto_Revoked_name, METH_VARARGS, crypto_Revoked_name_doc }
+ * for convenience
+ */
+#define ADD_METHOD(name) \
+ { #name, (PyCFunction)crypto_Revoked_##name, METH_VARARGS, crypto_Revoked_##name##_doc }
+#define ADD_KW_METHOD(name) \
+ { #name, (PyCFunction)crypto_Revoked_##name, METH_VARARGS | METH_KEYWORDS, crypto_Revoked_##name##_doc }
+static PyMethodDef crypto_Revoked_methods[] = {
+ ADD_METHOD(all_reasons),
+ ADD_METHOD(get_reason),
+ ADD_KW_METHOD(set_reason),
+ ADD_METHOD(get_rev_date),
+ ADD_METHOD(set_rev_date),
+ ADD_METHOD(get_serial),
+ ADD_KW_METHOD(set_serial),
+ { NULL, NULL }
+};
+#undef ADD_METHOD
+
+
+static PyObject *
+crypto_Revoked_getattr(crypto_RevokedObj *self, char *name) {
+ return Py_FindMethod(crypto_Revoked_methods, (PyObject *)self, name);
+}
+
+static void
+crypto_Revoked_dealloc(crypto_RevokedObj *self) {
+ X509_REVOKED_free(self->revoked);
+ self->revoked = NULL;
+
+ PyObject_Del(self);
+}
+
+static char crypto_Revoked_doc[] = "\n\
+Revoked() -> Revoked instance\n\
+\n\
+Create a new empty Revoked object.\n\
+\n\
+@returns: The Revoked object\n\
+";
+
+static PyObject* crypto_Revoked_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) {
+ if (!PyArg_ParseTuple(args, ":Revoked")) {
+ return NULL;
+ }
+
+ return (PyObject *)crypto_Revoked_New(X509_REVOKED_new());
+}
+
+PyTypeObject crypto_Revoked_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "Revoked",
+ sizeof(crypto_RevokedObj),
+ 0,
+ (destructor)crypto_Revoked_dealloc,
+ NULL, /* print */
+ (getattrfunc)crypto_Revoked_getattr,
+ NULL, /* setattr */
+ NULL, /* compare */
+ NULL, /* repr */
+ NULL, /* as_number */
+ NULL, /* as_sequence */
+ NULL, /* as_mapping */
+ NULL, /* hash */
+ NULL, /* call */
+ NULL, /* str */
+ NULL, /* getattro */
+ NULL, /* setattro */
+ NULL, /* as_buffer */
+ Py_TPFLAGS_DEFAULT,
+ crypto_Revoked_doc, /* doc */
+ NULL, /* traverse */
+ NULL, /* clear */
+ NULL, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ NULL, /* tp_iter */
+ NULL, /* tp_iternext */
+ crypto_Revoked_methods, /* tp_methods */
+ NULL, /* tp_members */
+ NULL, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ NULL, /* tp_descr_get */
+ NULL, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ NULL, /* tp_init */
+ NULL, /* tp_alloc */
+ crypto_Revoked_new, /* tp_new */
+};
+
+int init_crypto_revoked(PyObject *module) {
+ if(PyType_Ready(&crypto_Revoked_Type) < 0) {
+ return 0;
+ }
+
+ if (PyModule_AddObject(module, "Revoked", (PyObject *)&crypto_Revoked_Type) != 0) {
+ return 0;
+ }
+ return 1;
+}
diff --git a/src/crypto/revoked.h b/src/crypto/revoked.h
new file mode 100644
index 0000000..fb85ac6
--- /dev/null
+++ b/src/crypto/revoked.h
@@ -0,0 +1,18 @@
+#ifndef PyOpenSSL_crypto_REVOKED_H_
+#define PyOpenSSL_crypto_REVOKED_H_
+
+#include <Python.h>
+
+extern PyTypeObject crypto_Revoked_Type;
+
+#define crypto_Revoked_Check(v) ((v)->ob_type == &crypto_Revoked_Type)
+
+typedef struct {
+ PyObject_HEAD
+ X509_REVOKED *revoked;
+} crypto_RevokedObj;
+
+extern int init_crypto_revoked (PyObject *);
+extern crypto_RevokedObj * crypto_Revoked_New(X509_REVOKED *revoked);
+
+#endif
diff --git a/src/crypto/x509.c b/src/crypto/x509.c
index e089d40..daae351 100644
--- a/src/crypto/x509.c
+++ b/src/crypto/x509.c
@@ -335,8 +335,8 @@
return Py_None;
}
-static PyObject*
-_set_asn1_time(char *format, ASN1_TIME* timestamp, crypto_X509Obj *self, PyObject *args)
+PyObject*
+_set_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args)
{
char *when;
@@ -375,7 +375,7 @@
crypto_X509_set_notBefore(crypto_X509Obj *self, PyObject *args)
{
return _set_asn1_time(
- "s:set_notBefore", X509_get_notBefore(self->x509), self, args);
+ "s:set_notBefore", X509_get_notBefore(self->x509), args);
}
static char crypto_X509_set_notAfter_doc[] = "\n\
@@ -394,11 +394,11 @@
crypto_X509_set_notAfter(crypto_X509Obj *self, PyObject *args)
{
return _set_asn1_time(
- "s:set_notAfter", X509_get_notAfter(self->x509), self, args);
+ "s:set_notAfter", X509_get_notAfter(self->x509), args);
}
-static PyObject*
-_get_asn1_time(char *format, ASN1_TIME* timestamp, crypto_X509Obj *self, PyObject *args)
+PyObject*
+_get_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args)
{
ASN1_GENERALIZEDTIME *gt_timestamp = NULL;
PyObject *py_timestamp = NULL;
@@ -450,7 +450,7 @@
* X509_get_notBefore returns a borrowed reference.
*/
return _get_asn1_time(
- ":get_notBefore", X509_get_notBefore(self->x509), self, args);
+ ":get_notBefore", X509_get_notBefore(self->x509), args);
}
@@ -472,7 +472,7 @@
* X509_get_notAfter returns a borrowed reference.
*/
return _get_asn1_time(
- ":get_notAfter", X509_get_notAfter(self->x509), self, args);
+ ":get_notAfter", X509_get_notAfter(self->x509), args);
}
diff --git a/src/crypto/x509.h b/src/crypto/x509.h
index 40768cf..43e41eb 100644
--- a/src/crypto/x509.h
+++ b/src/crypto/x509.h
@@ -16,8 +16,6 @@
#include <Python.h>
#include <openssl/ssl.h>
-extern int init_crypto_x509 (PyObject *);
-
extern PyTypeObject crypto_X509_Type;
#define crypto_X509_Check(v) ((v)->ob_type == &crypto_X509_Type)
@@ -28,5 +26,9 @@
int dealloc;
} crypto_X509Obj;
+PyObject* _set_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args);
+PyObject* _get_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args);
+extern int init_crypto_x509 (PyObject *);
+
#endif
diff --git a/test/test_crypto.py b/test/test_crypto.py
index 77a0c85..ae99fc5 100644
--- a/test/test_crypto.py
+++ b/test/test_crypto.py
@@ -19,7 +19,8 @@
from OpenSSL.crypto import dump_certificate, load_certificate_request
from OpenSSL.crypto import dump_certificate_request, dump_privatekey
from OpenSSL.crypto import PKCS7Type, load_pkcs7_data
-from OpenSSL.crypto import PKCS12Type, load_pkcs12, PKCS12
+from OpenSSL.crypto import PKCS12, PKCS12Type, load_pkcs12
+from OpenSSL.crypto import CRL, Revoked, load_crl
from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType
from OpenSSL.test.util import TestCase
@@ -226,6 +227,18 @@
-----END PKCS7-----
"""
+crlData ="""\
+-----BEGIN X509 CRL-----
+MIIBWzCBxTANBgkqhkiG9w0BAQQFADBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMC
+SUwxEDAOBgNVBAcTB0NoaWNhZ28xEDAOBgNVBAoTB1Rlc3RpbmcxGDAWBgNVBAMT
+D1Rlc3RpbmcgUm9vdCBDQRcNMDkwNzI2MDQzNDU2WhcNMTIwOTI3MDI0MTUyWjA8
+MBUCAgOrGA8yMDA5MDcyNTIzMzQ1NlowIwICAQAYDzIwMDkwNzI1MjMzNDU2WjAM
+MAoGA1UdFQQDCgEEMA0GCSqGSIb3DQEBBAUAA4GBAEBt7xTs2htdD3d4ErrcGAw1
+4dKcVnIWTutoI7xxen26Wwvh8VCsT7i/UeP+rBl9rC/kfjWjzQk3/zleaarGTpBT
+0yp4HXRFFoRhhSE/hP+eteaPXRgrsNRLHe9ZDd69wmh7J1wMDb0m81RG7kqcbsid
+vrzEeLDRiiPl92dyyWmu
+-----END X509 CRL-----
+"""
class X509ExtTests(TestCase):
"""
@@ -916,6 +929,11 @@
# An invalid string results in a ValueError
self.assertRaises(ValueError, set, "foo bar")
+ # The wrong number of arguments results in a TypeError.
+ self.assertRaises(TypeError, set)
+ self.assertRaises(TypeError, set, "20040203040506Z", "20040203040506Z")
+ self.assertRaises(TypeError, get, "foo bar")
+
def test_set_notBefore(self):
"""
@@ -1324,6 +1342,7 @@
return ' '.join(map(cmdLineQuote, arguments))
+
def _runopenssl(pem, *args):
"""
Run the command line openssl tool with the given arguments and write
@@ -1532,6 +1551,313 @@
self.assertTrue(isinstance(nspki, NetscapeSPKIType))
+class RevokedTests(TestCase):
+ """
+ Tests for L{OpenSSL.crypto.Revoked}
+ """
+ def test_construction(self):
+ """
+ Confirm we can create L{OpenSSL.crypto.Revoked}. Check
+ that it is empty.
+ """
+ revoked = Revoked()
+ self.assertTrue( isinstance(revoked, Revoked) )
+ self.assertEqual( type(revoked), Revoked )
+ self.assertEqual( revoked.get_serial(), '00' )
+ self.assertEqual( revoked.get_rev_date(), None )
+ self.assertEqual( revoked.get_reason(), None )
+
+
+ def test_construction_wrong_args(self):
+ """
+ Calling L{OpenSSL.crypto.Revoked} with any arguments results
+ in a L{TypeError} being raised.
+ """
+ self.assertRaises(TypeError, Revoked, None)
+ self.assertRaises(TypeError, Revoked, 1)
+ self.assertRaises(TypeError, Revoked, "foo")
+
+
+ def test_serial(self):
+ """
+ Confirm we can set and get serial numbers from
+ L{OpenSSL.crypto.Revoked}. Confirm errors are handled
+ with grace.
+ """
+ revoked = Revoked()
+ ret = revoked.set_serial('10b')
+ self.assertEqual( ret, None )
+ ser = revoked.get_serial()
+ self.assertEqual( ser, '010B' )
+
+ revoked.set_serial('31ppp') # a type error would be nice
+ ser = revoked.get_serial()
+ self.assertEqual( ser, '31' )
+
+ self.assertRaises(ValueError, revoked.set_serial, 'pqrst')
+ self.assertRaises(TypeError, revoked.set_serial, 100)
+ self.assertRaises(TypeError, revoked.get_serial, 1)
+ self.assertRaises(TypeError, revoked.get_serial, None)
+ self.assertRaises(TypeError, revoked.get_serial, "")
+
+
+ def test_date(self):
+ """
+ Confirm we can set and get revocation dates from
+ L{OpenSSL.crypto.Revoked}. Confirm errors are handled
+ with grace.
+ """
+ revoked = Revoked()
+ date = revoked.get_rev_date()
+ self.assertEqual( date, None )
+
+ now = datetime.now().strftime("%Y%m%d%H%M%SZ")
+ ret = revoked.set_rev_date(now)
+ self.assertEqual( ret, None )
+ date = revoked.get_rev_date()
+ self.assertEqual( date, now )
+
+
+ def test_reason(self):
+ """
+ Confirm we can set and get revocation reasons from
+ L{OpenSSL.crypto.Revoked}. The "get" need to work
+ as "set". Likewise, each reason of all_reasons() must work.
+ """
+ revoked = Revoked()
+ for r in revoked.all_reasons():
+ for x in xrange(2):
+ ret = revoked.set_reason(r)
+ self.assertEqual( ret, None )
+ reason = revoked.get_reason()
+ self.assertEqual( reason.lower().replace(' ',''),
+ r.lower().replace(' ','') )
+ r = reason # again with the resp of get
+
+ revoked.set_reason(None)
+ self.assertEqual(revoked.get_reason(), None)
+
+
+ def test_set_reason_wrong_arguments(self):
+ """
+ Calling L{OpenSSL.crypto.Revoked.set_reason} with other than
+ one argument, or an argument which isn't a valid reason,
+ results in L{TypeError} or L{ValueError} being raised.
+ """
+ revoked = Revoked()
+ self.assertRaises(TypeError, revoked.set_reason, 100)
+ self.assertRaises(ValueError, revoked.set_reason, 'blue')
+
+
+ def test_get_reason_wrong_arguments(self):
+ """
+ Calling L{OpenSSL.crypto.Revoked.get_reason} with any
+ arguments results in L{TypeError} being raised.
+ """
+ revoked = Revoked()
+ self.assertRaises(TypeError, revoked.get_reason, None)
+ self.assertRaises(TypeError, revoked.get_reason, 1)
+ self.assertRaises(TypeError, revoked.get_reason, "foo")
+
+
+
+class CRLTests(TestCase):
+ """
+ Tests for L{OpenSSL.crypto.CRL}
+ """
+ cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
+ pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+
+ def test_construction(self):
+ """
+ Confirm we can create L{OpenSSL.crypto.CRL}. Check
+ that it is empty
+ """
+ crl = CRL()
+ self.assertTrue( isinstance(crl, CRL) )
+ self.assertEqual(crl.get_revoked(), None)
+
+
+ def test_construction_wrong_args(self):
+ """
+ Calling L{OpenSSL.crypto.CRL} with any number of arguments
+ results in a L{TypeError} being raised.
+ """
+ self.assertRaises(TypeError, CRL, 1)
+ self.assertRaises(TypeError, CRL, "")
+ self.assertRaises(TypeError, CRL, None)
+
+
+ def test_export(self):
+ """
+ Use python to create a simple CRL with a revocation, and export
+ the CRL in formats of PEM, DER and text. Those outputs are verified
+ with the openssl program.
+ """
+ crl = CRL()
+ revoked = Revoked()
+ now = datetime.now().strftime("%Y%m%d%H%M%SZ")
+ revoked.set_rev_date(now)
+ revoked.set_serial('3ab')
+ revoked.set_reason('sUpErSeDEd')
+ crl.add_revoked(revoked)
+
+ # PEM format
+ dumped_crl = crl.export(self.cert, self.pkey, days=20)
+ text = _runopenssl(dumped_crl, "crl", "-noout", "-text")
+ text.index('Serial Number: 03AB')
+ text.index('Superseded')
+ text.index('Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=Testing Root CA')
+
+ # DER format
+ dumped_crl = crl.export(self.cert, self.pkey, FILETYPE_ASN1)
+ text = _runopenssl(dumped_crl, "crl", "-noout", "-text", "-inform", "DER")
+ text.index('Serial Number: 03AB')
+ text.index('Superseded')
+ text.index('Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=Testing Root CA')
+
+ # text format
+ dumped_text = crl.export(self.cert, self.pkey, type=FILETYPE_TEXT)
+ self.assertEqual(text, dumped_text)
+
+
+ def test_add_revoked_keyword(self):
+ """
+ L{OpenSSL.CRL.add_revoked} accepts its single argument as the
+ I{revoked} keyword argument.
+ """
+ crl = CRL()
+ revoked = Revoked()
+ crl.add_revoked(revoked=revoked)
+ self.assertTrue(isinstance(crl.get_revoked()[0], Revoked))
+
+
+ def test_export_wrong_args(self):
+ """
+ Calling L{OpenSSL.CRL.export} with fewer than two or more than
+ four arguments, or with arguments other than the certificate,
+ private key, integer file type, and integer number of days it
+ expects, results in a L{TypeError} being raised.
+ """
+ crl = CRL()
+ self.assertRaises(TypeError, crl.export)
+ self.assertRaises(TypeError, crl.export, self.cert)
+ self.assertRaises(TypeError, crl.export, self.cert, self.pkey, FILETYPE_PEM, 10, "foo")
+
+ self.assertRaises(TypeError, crl.export, None, self.pkey, FILETYPE_PEM, 10)
+ self.assertRaises(TypeError, crl.export, self.cert, None, FILETYPE_PEM, 10)
+ self.assertRaises(TypeError, crl.export, self.cert, self.pkey, None, 10)
+ self.assertRaises(TypeError, crl.export, self.cert, FILETYPE_PEM, None)
+
+
+ def test_export_unknown_filetype(self):
+ """
+ Calling L{OpenSSL.CRL.export} with a file type other than
+ L{FILETYPE_PEM}, L{FILETYPE_ASN1}, or L{FILETYPE_TEXT} results
+ in a L{ValueError} being raised.
+ """
+ crl = CRL()
+ self.assertRaises(ValueError, crl.export, self.cert, self.pkey, 100, 10)
+
+
+ def test_get_revoked(self):
+ """
+ Use python to create a simple CRL with two revocations.
+ Get back the L{Revoked} using L{OpenSSL.CRL.get_revoked} and
+ verify them.
+ """
+ crl = CRL()
+
+ revoked = Revoked()
+ now = datetime.now().strftime("%Y%m%d%H%M%SZ")
+ revoked.set_rev_date(now)
+ revoked.set_serial('3ab')
+ crl.add_revoked(revoked)
+ revoked.set_serial('100')
+ revoked.set_reason('sUpErSeDEd')
+ crl.add_revoked(revoked)
+
+ revs = crl.get_revoked()
+ self.assertEqual(len(revs), 2)
+ self.assertEqual(type(revs[0]), Revoked)
+ self.assertEqual(type(revs[1]), Revoked)
+ self.assertEqual(revs[0].get_serial(), '03AB')
+ self.assertEqual(revs[1].get_serial(), '0100')
+ self.assertEqual(revs[0].get_rev_date(), now)
+ self.assertEqual(revs[1].get_rev_date(), now)
+
+
+ def test_get_revoked_wrong_args(self):
+ """
+ Calling L{OpenSSL.CRL.get_revoked} with any arguments results
+ in a L{TypeError} being raised.
+ """
+ crl = CRL()
+ self.assertRaises(TypeError, crl.get_revoked, None)
+ self.assertRaises(TypeError, crl.get_revoked, 1)
+ self.assertRaises(TypeError, crl.get_revoked, "")
+ self.assertRaises(TypeError, crl.get_revoked, "", 1, None)
+
+
+ def test_add_revoked_wrong_args(self):
+ """
+ Calling L{OpenSSL.CRL.add_revoked} with other than one
+ argument results in a L{TypeError} being raised.
+ """
+ crl = CRL()
+ self.assertRaises(TypeError, crl.add_revoked)
+ self.assertRaises(TypeError, crl.add_revoked, 1, 2)
+ self.assertRaises(TypeError, crl.add_revoked, "foo", "bar")
+
+
+ def test_load_crl(self):
+ """
+ Load a known CRL and inspect its revocations. Both
+ PEM and DER formats are loaded.
+ """
+ crl = load_crl(FILETYPE_PEM, crlData)
+ revs = crl.get_revoked()
+ self.assertEqual(len(revs), 2)
+ self.assertEqual(revs[0].get_serial(), '03AB')
+ self.assertEqual(revs[0].get_reason(), None)
+ self.assertEqual(revs[1].get_serial(), '0100')
+ self.assertEqual(revs[1].get_reason(), 'Superseded')
+
+ der = _runopenssl(crlData, "crl", "-outform", "DER")
+ crl = load_crl(FILETYPE_ASN1, der)
+ revs = crl.get_revoked()
+ self.assertEqual(len(revs), 2)
+ self.assertEqual(revs[0].get_serial(), '03AB')
+ self.assertEqual(revs[0].get_reason(), None)
+ self.assertEqual(revs[1].get_serial(), '0100')
+ self.assertEqual(revs[1].get_reason(), 'Superseded')
+
+
+ def test_load_crl_wrong_args(self):
+ """
+ Calling L{OpenSSL.crypto.load_crl} with other than two
+ arguments results in a L{TypeError} being raised.
+ """
+ self.assertRaises(TypeError, load_crl)
+ self.assertRaises(TypeError, load_crl, FILETYPE_PEM)
+ self.assertRaises(TypeError, load_crl, FILETYPE_PEM, crlData, None)
+
+
+ def test_load_crl_bad_filetype(self):
+ """
+ Calling L{OpenSSL.crypto.load_crl} with an unknown file type
+ raises a L{ValueError}.
+ """
+ self.assertRaises(ValueError, load_crl, 100, crlData)
+
+
+ def test_load_crl_bad_data(self):
+ """
+ Calling L{OpenSSL.crypto.load_crl} with file data which can't
+ be loaded raises a L{OpenSSL.crypto.Error}.
+ """
+ self.assertRaises(Error, load_crl, FILETYPE_PEM, "hello, world")
+
class SignVerifyTests(TestCase):
"""