Merge lp:~rick-fdd/pyopenssl/crl_and_revoked into lp:~exarkun/pyopenssl/trunk

Proposed by sebvieira
Status: Merged
Merged at revision: 129
Proposed branch: lp:~rick-fdd/pyopenssl/crl_and_revoked
Merge into: lp:~exarkun/pyopenssl/trunk
Diff against target: 1316 lines (+1131/-11)
11 files modified
doc/pyOpenSSL.tex (+68/-0)
setup.py (+2/-0)
src/crypto/crl.c (+294/-0)
src/crypto/crl.h (+19/-0)
src/crypto/crypto.c (+52/-1)
src/crypto/crypto.h (+2/-0)
src/crypto/revoked.c (+451/-0)
src/crypto/revoked.h (+18/-0)
src/crypto/x509.c (+8/-8)
src/crypto/x509.h (+4/-2)
test/test_crypto.py (+213/-0)
To merge this branch: bzr merge lp:~rick-fdd/pyopenssl/crl_and_revoked
Reviewer Review Type Date Requested Status
Jean-Paul Calderone Pending
Review via email: mp+14640@code.launchpad.net
To post a comment you must log in.
Revision history for this message
sebvieira (sebvieira) wrote :

Tested and works perfectly. Test units and documentation are present. Functionality would add a lot to pyOpenSSL implementation.

Revision history for this message
Jean-Paul Calderone (exarkun) wrote :

I spent a good chunk of time on this today. Some changes have been necessary, primarily to improve test coverage, but also to get the code working on Windows. I haven't finished looking at all the code yet. You can find my changes in <lp:~exarkun/pyopenssl/crl_and_revoked>.

One thing which I probably won't undertake, but which might make sense, is for the Revoked class to be a Python class, not an extension class. This would greatly simplify the implementation, and I don't think it would introduce any problems, since the only thing Revoked instances are used for configuring CRLs (that is, they aren't passed directly into any other OpenSSL APIs) and because their identity is already discarded when they pass through a CRL object. However, I'm definitely not familiar with all of OpenSSL's CRL-related APIs.

Revision history for this message
rick_dean (rick-fdd) wrote :

If by "python class" you mean not just that Revoked object would not point to an underlying OpenSSL C datatype, but instead would be written in Python without C, I totally agree and did not think of it at the time.

--
Rick

On Sat, Jan 30, 2010 at 08:56:16PM -0000, Jean-Paul Calderone wrote:
> I spent a good chunk of time on this today. Some changes have been necessary, primarily to improve test coverage, but also to get the code working on Windows. I haven't finished looking at all the code yet. You can find my changes in <lp:~exarkun/pyopenssl/crl_and_revoked>.
>
> One thing which I probably won't undertake, but which might make sense, is for the Revoked class to be a Python class, not an extension class. This would greatly simplify the implementation, and I don't think it would introduce any problems, since the only thing Revoked instances are used for configuring CRLs (that is, they aren't passed directly into any other OpenSSL APIs) and because their identity is already discarded when they pass through a CRL object. However, I'm definitely not familiar with all of OpenSSL's CRL-related APIs.
>
> --
> https://code.launchpad.net/~rick-fdd/pyopenssl/crl_and_revoked/+merge/14640
> You are the owner of lp:~rick-fdd/pyopenssl/crl_and_revoked.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'doc/pyOpenSSL.tex'
2--- doc/pyOpenSSL.tex 2009-10-23 07:51:07 +0000
3+++ doc/pyOpenSSL.tex 2009-11-09 06:00:31 +0000
4@@ -207,6 +207,14 @@
5 method.
6 \end{classdesc}
7
8+\begin{classdesc}{CRL}{}
9+A class representing Certifcate Revocation List objects.
10+\end{classdesc}
11+
12+\begin{classdesc}{Revoked}{}
13+A class representing Revocation objects of CRL.
14+\end{classdesc}
15+
16 \begin{datadesc}{FILETYPE_PEM}
17 \dataline{FILETYPE_ASN1}
18 File type constants.
19@@ -259,6 +267,12 @@
20 pass phrase.
21 \end{funcdesc}
22
23+\begin{funcdesc}{load_crl}{type, buffer}
24+Load Certificate Revocation List (CRL) data from a string \var{buffer}.
25+\var{buffer} encoded with the type \var{type}. The type \var{type}
26+must either \constant{FILETYPE_PEM} or \constant{FILETYPE_ASN1}).
27+\end{funcdesc}
28+
29 \begin{funcdesc}{load_pkcs7_data}{type, buffer}
30 Load pkcs7 data from the string \var{buffer} encoded with the type \var{type}.
31 \end{funcdesc}
32@@ -612,6 +626,60 @@
33 Verify the NetscapeSPKI object using the given \var{key}.
34 \end{methoddesc}
35
36+\subsubsection{CRL objects \label{crl}}
37+
38+CRL objects have the following methods:
39+
40+\begin{methoddesc}[CRL]{add_revoked}{revoked}
41+Add a Revoked object to the CRL, by value not reference.
42+\end{methoddesc}
43+
44+\begin{methoddesc}[CRL]{export}{cert, key\optional{, type=FILETYPE_PEM}\optional{, days=100}}
45+Use \var{cert} and \var{key} to sign the CRL and return the CRL as a string.
46+\var{days} is the number of days before the next CRL is due.
47+\end{methoddesc}
48+
49+\begin{methoddesc}[CRL]{get_revoked}{}
50+Return a tuple of Revoked objects, by value not reference.
51+\end{methoddesc}
52+
53+\subsubsection{Revoked objects \label{revoked}}
54+
55+Revoked objects have the following methods:
56+
57+\begin{methoddesc}[Revoked]{all_reasons}{}
58+Return a list of all supported reasons.
59+\end{methoddesc}
60+
61+\begin{methoddesc}[Revoked]{get_reason}{}
62+Return the revocation reason as a str. Can be
63+None, which differs from "Unspecified".
64+\end{methoddesc}
65+
66+\begin{methoddesc}[Revoked]{get_rev_date}{}
67+Return the revocation date as a str.
68+The string is formatted as an ASN1 GENERALIZEDTIME.
69+\end{methoddesc}
70+
71+\begin{methoddesc}[Revoked]{get_serial}{}
72+Return a str containing a hex number of the serial of the revoked certificate.
73+\end{methoddesc}
74+
75+\begin{methoddesc}[Revoked]{set_reason}{reason}
76+Set the revocation reason. \var{reason} must
77+be None or a string, but the values are limited.
78+Spaces and case are ignored. See \method{all_reasons}.
79+\end{methoddesc}
80+
81+\begin{methoddesc}[Revoked]{set_rev_date}{date}
82+Set the revocation date.
83+The string is formatted as an ASN1 GENERALIZEDTIME.
84+\end{methoddesc}
85+
86+\begin{methoddesc}[Revoked]{set_serial}{serial}
87+\var{serial} is a string containing a hex number of the serial of the revoked certificate.
88+\end{methoddesc}
89+
90
91 % % % rand module
92
93
94=== modified file 'setup.py'
95--- setup.py 2009-07-24 13:37:38 +0000
96+++ setup.py 2009-11-09 06:00:30 +0000
97@@ -23,12 +23,14 @@
98 'src/crypto/x509store.c', 'src/crypto/x509req.c',
99 'src/crypto/x509ext.c', 'src/crypto/pkcs7.c',
100 'src/crypto/pkcs12.c', 'src/crypto/netscape_spki.c',
101+ 'src/crypto/revoked.c', 'src/crypto/crl.c',
102 'src/util.c']
103 crypto_dep = ['src/crypto/crypto.h', 'src/crypto/x509.h',
104 'src/crypto/x509name.h', 'src/crypto/pkey.h',
105 'src/crypto/x509store.h', 'src/crypto/x509req.h',
106 'src/crypto/x509ext.h', 'src/crypto/pkcs7.h',
107 'src/crypto/pkcs12.h', 'src/crypto/netscape_spki.h',
108+ 'src/crypto/revoked.h', 'src/crypto/crl.h',
109 'src/util.h']
110 rand_src = ['src/rand/rand.c', 'src/util.c']
111 rand_dep = ['src/util.h']
112
113=== added file 'src/crypto/crl.c'
114--- src/crypto/crl.c 1970-01-01 00:00:00 +0000
115+++ src/crypto/crl.c 2009-11-09 06:00:31 +0000
116@@ -0,0 +1,294 @@
117+#include <Python.h>
118+#define crypto_MODULE
119+#include "crypto.h"
120+
121+
122+static X509_REVOKED * X509_REVOKED_dup(X509_REVOKED *orig)
123+{
124+ X509_REVOKED *dupe = NULL;
125+
126+ dupe = X509_REVOKED_new();
127+ if(dupe == NULL)
128+ return NULL;
129+ if(orig->serialNumber)
130+ {
131+ dupe->serialNumber = M_ASN1_INTEGER_dup(orig->serialNumber);
132+ }
133+ if(orig->revocationDate)
134+ {
135+ dupe->revocationDate = M_ASN1_INTEGER_dup(orig->revocationDate);
136+ }
137+ if(orig->extensions)
138+ {
139+ STACK_OF(X509_EXTENSION) *sk = NULL;
140+ X509_EXTENSION * ext;
141+ int j;
142+
143+ sk = sk_X509_EXTENSION_new_null();
144+ for(j = 0; j < sk_X509_EXTENSION_num(orig->extensions); j++) {
145+ ext = sk_X509_EXTENSION_value(orig->extensions, j);
146+ ext = X509_EXTENSION_dup(ext);
147+ sk_X509_EXTENSION_push(sk, ext);
148+ }
149+ dupe->extensions = sk;
150+ }
151+ dupe->sequence = orig->sequence;
152+ return dupe;
153+}
154+
155+static char crypto_CRL_get_revoked_doc[] = "\n\
156+Return revoked portion of the CRL structure (by value\n\
157+not reference).\n\
158+\n\
159+@return: A tuple of Revoked objects.\n\
160+";
161+static PyObject *
162+crypto_CRL_get_revoked(crypto_CRLObj *self, PyObject *args)
163+{
164+ int j, num_rev;
165+ X509_REVOKED *r = NULL;
166+ PyObject *obj = NULL, *rev_obj;
167+
168+ if (!PyArg_ParseTuple(args, ":get_revoked"))
169+ return NULL;
170+
171+ num_rev = sk_X509_REVOKED_num(self->crl->crl->revoked);
172+ if(num_rev < 0)
173+ {
174+ Py_INCREF(Py_None);
175+ return Py_None;
176+ }
177+ if ((obj = PyTuple_New(num_rev)) == NULL)
178+ return NULL;
179+
180+ for(j = 0; j < num_rev; j++)
181+ {
182+ r = sk_X509_REVOKED_value(self->crl->crl->revoked, j);
183+ r = X509_REVOKED_dup(r);
184+ if( r == NULL )
185+ goto error;
186+ rev_obj = (PyObject *) crypto_Revoked_New(r);
187+ if( rev_obj == NULL )
188+ goto error;
189+ r = NULL; /* it's now owned by rev_obj */
190+ PyTuple_SET_ITEM(obj, j, rev_obj);
191+ }
192+ return obj;
193+
194+ error:
195+ if(r)
196+ X509_REVOKED_free(r);
197+ Py_XDECREF(obj);
198+ return NULL;
199+}
200+
201+static char crypto_CRL_add_revoked_doc[] = "\n\
202+Add a revoked (by value not reference) to the CRL structure\n\
203+\n\
204+@param cert: The new revoked.\n\
205+@type cert: L{X509}\n\
206+@return: None\n\
207+";
208+static PyObject *
209+crypto_CRL_add_revoked(crypto_CRLObj *self, PyObject *args, PyObject *keywds)
210+{
211+ crypto_RevokedObj * rev_obj = NULL;
212+ static char *kwlist[] = {"revoked", NULL};
213+ X509_REVOKED * dup;
214+
215+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!:add_revoked",
216+ kwlist, &crypto_Revoked_Type, &rev_obj))
217+ return NULL;
218+
219+ dup = X509_REVOKED_dup( rev_obj->revoked );
220+ if(dup == NULL)
221+ return NULL;
222+ X509_CRL_add0_revoked(self->crl, dup);
223+
224+ Py_INCREF(Py_None);
225+ return Py_None;
226+}
227+
228+static char crypto_CRL_export_doc[] = "\n\
229+Export a CRL as a string\n\
230+\n\
231+@param cert: Used to sign CRL.\n\
232+@type cert: L{X509}\n\
233+@param key: Used to sign CRL.\n\
234+@type key: L{PKey}\n\
235+@return: None\n\
236+";
237+static PyObject *
238+crypto_CRL_export(crypto_CRLObj *self, PyObject *args, PyObject *keywds)
239+{
240+ int ret, buf_len, type = X509_FILETYPE_PEM, days = 100;
241+ char *temp;
242+ BIO *bio;
243+ PyObject *buffer;
244+ crypto_PKeyObj *key;
245+ ASN1_TIME *tmptm;
246+ crypto_X509Obj *x509;
247+ static char *kwlist[] = {"cert", "key", "type", "days", NULL};
248+
249+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!O!|ii:dump_crl", kwlist,
250+ &crypto_X509_Type, &x509,
251+ &crypto_PKey_Type, &key, &type, &days))
252+ return NULL;
253+
254+ bio=BIO_new(BIO_s_mem());
255+ tmptm = ASN1_TIME_new();
256+ if (!tmptm)
257+ return 0;
258+ X509_gmtime_adj(tmptm,0);
259+ X509_CRL_set_lastUpdate(self->crl, tmptm);
260+ X509_gmtime_adj(tmptm,days*24*60*60);
261+ X509_CRL_set_nextUpdate(self->crl, tmptm);
262+ ASN1_TIME_free(tmptm);
263+ X509_CRL_set_issuer_name(self->crl, X509_get_subject_name(x509->x509));
264+ X509_CRL_sign(self->crl, key->pkey, EVP_md5());
265+ switch (type)
266+ {
267+ case X509_FILETYPE_PEM:
268+ ret = PEM_write_bio_X509_CRL(bio, self->crl);
269+ break;
270+
271+ case X509_FILETYPE_ASN1:
272+ ret = (int) i2d_X509_CRL_bio(bio, self->crl);
273+ break;
274+
275+ case X509_FILETYPE_TEXT:
276+ ret = X509_CRL_print(bio, self->crl);
277+ break;
278+
279+ default:
280+ PyErr_SetString(PyExc_ValueError,
281+ "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT");
282+ return NULL;
283+ };
284+ if( ! ret )
285+ {
286+ exception_from_error_queue(crypto_Error);
287+ BIO_free(bio);
288+ return NULL;
289+ }
290+ buf_len = BIO_get_mem_data(bio, &temp);
291+ buffer = PyString_FromStringAndSize(temp, buf_len);
292+ BIO_free(bio);
293+ return buffer;
294+}
295+
296+crypto_CRLObj *
297+crypto_CRL_New(X509_CRL *crl)
298+{
299+ crypto_CRLObj *self;
300+
301+ self = PyObject_New(crypto_CRLObj, &crypto_CRL_Type);
302+ if (self==NULL)
303+ return NULL;
304+ self->crl = crl;
305+ return self;
306+}
307+
308+/*
309+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
310+ * { 'name', (PyCFunction)crypto_CRL_name, METH_VARARGS, crypto_CRL_name_doc }
311+ * for convenience
312+ */
313+#define ADD_METHOD(name) \
314+ { #name, (PyCFunction)crypto_CRL_##name, METH_VARARGS, crypto_CRL_##name##_doc }
315+#define ADD_KW_METHOD(name) \
316+ { #name, (PyCFunction)crypto_CRL_##name, METH_VARARGS | METH_KEYWORDS, crypto_CRL_##name##_doc }
317+static PyMethodDef crypto_CRL_methods[] =
318+{
319+ ADD_KW_METHOD(add_revoked),
320+ ADD_METHOD(get_revoked),
321+ ADD_KW_METHOD(export),
322+ { NULL, NULL }
323+};
324+#undef ADD_METHOD
325+
326+
327+static PyObject *
328+crypto_CRL_getattr(crypto_CRLObj *self, char *name)
329+{
330+ return Py_FindMethod(crypto_CRL_methods, (PyObject *)self, name);
331+}
332+
333+static void
334+crypto_CRL_dealloc(crypto_CRLObj *self)
335+{
336+ X509_CRL_free(self->crl);
337+ self->crl = NULL;
338+
339+ PyObject_Del(self);
340+}
341+
342+static char crypto_CRL_doc[] = "\n\
343+CRL() -> CRL instance\n\
344+\n\
345+Create a new empty CRL object.\n\
346+\n\
347+@returns: The CRL object\n\
348+";
349+
350+static PyObject* crypto_CRL_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) {
351+ if (!PyArg_ParseTuple(args, ":CRL")) {
352+ return NULL;
353+ }
354+
355+ return (PyObject *)crypto_CRL_New(X509_CRL_new());
356+}
357+
358+PyTypeObject crypto_CRL_Type = {
359+ PyObject_HEAD_INIT(NULL)
360+ 0,
361+ "CRL",
362+ sizeof(crypto_CRLObj),
363+ 0,
364+ (destructor)crypto_CRL_dealloc,
365+ NULL, /* print */
366+ (getattrfunc)crypto_CRL_getattr,
367+ NULL, /* setattr */
368+ NULL, /* compare */
369+ NULL, /* repr */
370+ NULL, /* as_number */
371+ NULL, /* as_sequence */
372+ NULL, /* as_mapping */
373+ NULL, /* hash */
374+ NULL, /* call */
375+ NULL, /* str */
376+ NULL, /* getattro */
377+ NULL, /* setattro */
378+ NULL, /* as_buffer */
379+ Py_TPFLAGS_DEFAULT,
380+ crypto_CRL_doc, /* doc */
381+ NULL, /* traverse */
382+ NULL, /* clear */
383+ NULL, /* tp_richcompare */
384+ 0, /* tp_weaklistoffset */
385+ NULL, /* tp_iter */
386+ NULL, /* tp_iternext */
387+ crypto_CRL_methods, /* tp_methods */
388+ NULL, /* tp_members */
389+ NULL, /* tp_getset */
390+ NULL, /* tp_base */
391+ NULL, /* tp_dict */
392+ NULL, /* tp_descr_get */
393+ NULL, /* tp_descr_set */
394+ 0, /* tp_dictoffset */
395+ NULL, /* tp_init */
396+ NULL, /* tp_alloc */
397+ crypto_CRL_new, /* tp_new */
398+};
399+
400+int init_crypto_crl(PyObject *module) {
401+ if(PyType_Ready(&crypto_CRL_Type) < 0) {
402+ return 0;
403+ }
404+
405+ if (PyModule_AddObject(module, "CRL", (PyObject *)&crypto_CRL_Type) != 0) {
406+ return 0;
407+ }
408+ return 1;
409+}
410+
411
412=== added file 'src/crypto/crl.h'
413--- src/crypto/crl.h 1970-01-01 00:00:00 +0000
414+++ src/crypto/crl.h 2009-11-09 06:00:31 +0000
415@@ -0,0 +1,19 @@
416+#ifndef PyOpenSSL_crypto_CRL_H_
417+#define PyOpenSSL_crypto_CRL_H_
418+
419+#include <Python.h>
420+
421+extern int init_crypto_crl (PyObject *);
422+
423+extern PyTypeObject crypto_CRL_Type;
424+
425+#define crypto_CRL_Check(v) ((v)->ob_type == &crypto_CRL_Type)
426+
427+typedef struct {
428+ PyObject_HEAD
429+ X509_CRL *crl;
430+} crypto_CRLObj;
431+
432+crypto_CRLObj * crypto_CRL_New(X509_CRL *crl);
433+
434+#endif
435
436=== modified file 'src/crypto/crypto.c'
437--- src/crypto/crypto.c 2009-07-17 17:50:12 +0000
438+++ src/crypto/crypto.c 2009-11-09 06:00:31 +0000
439@@ -431,6 +431,53 @@
440 return buffer;
441 }
442
443+static char crypto_load_crl_doc[] = "\n\
444+Load a certificate revocation list from a buffer\n\
445+\n\
446+@param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\
447+@param buffer: The buffer the CRL is stored in\n\
448+\n\
449+@return: The PKey object\n\
450+";
451+
452+static PyObject *
453+crypto_load_crl(PyObject *spam, PyObject *args)
454+{
455+ int type, len;
456+ char *buffer;
457+ BIO *bio;
458+ X509_CRL *crl;
459+
460+ if (!PyArg_ParseTuple(args, "is#:load_crl", &type, &buffer, &len))
461+ return NULL;
462+
463+ bio = BIO_new_mem_buf(buffer, len);
464+ switch (type)
465+ {
466+ case X509_FILETYPE_PEM:
467+ crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL);
468+ break;
469+
470+ case X509_FILETYPE_ASN1:
471+ crl = d2i_X509_CRL_bio(bio, NULL);
472+ break;
473+
474+ default:
475+ PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
476+ BIO_free(bio);
477+ return NULL;
478+ }
479+ BIO_free(bio);
480+
481+ if (crl == NULL)
482+ {
483+ exception_from_error_queue(crypto_Error);
484+ return NULL;
485+ }
486+
487+ return (PyObject *)crypto_CRL_New(crl);
488+}
489+
490 static char crypto_load_pkcs7_data_doc[] = "\n\
491 Load pkcs7 data from a buffer\n\
492 \n\
493@@ -555,6 +602,7 @@
494 { "dump_certificate", (PyCFunction)crypto_dump_certificate, METH_VARARGS, crypto_dump_certificate_doc },
495 { "load_certificate_request", (PyCFunction)crypto_load_certificate_request, METH_VARARGS, crypto_load_certificate_request_doc },
496 { "dump_certificate_request", (PyCFunction)crypto_dump_certificate_request, METH_VARARGS, crypto_dump_certificate_request_doc },
497+ { "load_crl", (PyCFunction)crypto_load_crl, METH_VARARGS, crypto_load_crl_doc },
498 { "load_pkcs7_data", (PyCFunction)crypto_load_pkcs7_data, METH_VARARGS, crypto_load_pkcs7_data_doc },
499 { "load_pkcs12", (PyCFunction)crypto_load_pkcs12, METH_VARARGS, crypto_load_pkcs12_doc },
500 { "X509_verify_cert_error_string", (PyCFunction)crypto_X509_verify_cert_error_string, METH_VARARGS, crypto_X509_verify_cert_error_string_doc },
501@@ -696,7 +744,10 @@
502 goto error;
503 if (!init_crypto_netscape_spki(module))
504 goto error;
505-
506+ if (!init_crypto_crl(module))
507+ goto error;
508+ if (!init_crypto_revoked(module))
509+ goto error;
510 error:
511 ;
512 }
513
514=== modified file 'src/crypto/crypto.h'
515--- src/crypto/crypto.h 2009-07-17 20:06:12 +0000
516+++ src/crypto/crypto.h 2009-11-09 06:00:31 +0000
517@@ -23,6 +23,8 @@
518 #include "x509ext.h"
519 #include "pkcs7.h"
520 #include "pkcs12.h"
521+#include "crl.h"
522+#include "revoked.h"
523 #include "../util.h"
524
525 extern PyObject *crypto_Error;
526
527=== added file 'src/crypto/revoked.c'
528--- src/crypto/revoked.c 1970-01-01 00:00:00 +0000
529+++ src/crypto/revoked.c 2009-11-09 06:00:31 +0000
530@@ -0,0 +1,451 @@
531+#include <Python.h>
532+#define crypto_MODULE
533+#include "crypto.h"
534+
535+
536+/* http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_ */
537+/* which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches */
538+/* OCSP_crl_reason_str. We use the latter, just like the command line program. */
539+static const char *crl_reasons[] = {
540+ "unspecified",
541+ "keyCompromise",
542+ "CACompromise",
543+ "affiliationChanged",
544+ "superseded",
545+ "cessationOfOperation",
546+ "certificateHold",
547+ NULL,
548+ "removeFromCRL",
549+};
550+
551+#define NUM_REASONS (sizeof(crl_reasons) / sizeof(char *))
552+
553+static char crypto_Revoked_all_reasons_doc[] = "\n\
554+Return a list of all the supported reason strings.\n\
555+\n\
556+@return: A list of reason strings.\n\
557+";
558+static PyObject *
559+crypto_Revoked_all_reasons(crypto_RevokedObj *self, PyObject *args)
560+{
561+ PyObject *list, *str;
562+ int j;
563+
564+ list = PyList_New(0);
565+ for (j = 0; j < NUM_REASONS; j++)
566+ {
567+ if( crl_reasons[j] )
568+ {
569+ str = PyString_FromString(crl_reasons[j]);
570+ PyList_Append(list, str);
571+ Py_DECREF(str);
572+ }
573+ }
574+ return list;
575+}
576+
577+static PyObject *
578+X509_EXTENSION_value_to_PyString(X509_EXTENSION *ex)
579+{
580+ BIO *bio = NULL;
581+ PyObject *str = NULL;
582+ int str_len;
583+ char *tmp_str;
584+
585+ /* Create a openssl BIO buffer */
586+ bio = BIO_new(BIO_s_mem());
587+ if (bio == NULL)
588+ goto err;
589+
590+ /* These are not the droids you are looking for. */
591+ if(!X509V3_EXT_print(bio, ex, 0, 0))
592+ if(M_ASN1_OCTET_STRING_print(bio, ex->value) == 0)
593+ goto err;
594+
595+ /* Convert to a Python string. */
596+ str_len = BIO_get_mem_data(bio, &tmp_str);
597+ str = PyString_FromStringAndSize(tmp_str, str_len);
598+
599+ /* Cleanup */
600+ BIO_free(bio);
601+ return str;
602+
603+ err:
604+ if(bio) {
605+ BIO_free(bio);
606+ }
607+ if(str) {
608+ Py_DECREF(str);
609+ }
610+ return NULL;
611+}
612+
613+static void
614+delete_reason(STACK_OF(X509_EXTENSION) *sk)
615+{
616+ X509_EXTENSION * ext;
617+ int j;
618+
619+ for(j = 0; j < sk_X509_EXTENSION_num(sk); j++) {
620+ ext = sk_X509_EXTENSION_value(sk, j);
621+ if ( OBJ_obj2nid(ext->object) == NID_crl_reason) {
622+ X509_EXTENSION_free(ext);
623+ (void) sk_X509_EXTENSION_delete(sk, j);
624+ break;
625+ }
626+ }
627+}
628+
629+static int
630+reason_str_to_code(const char * reason_str)
631+{
632+ int reason_code = -1, j;
633+ char *spaceless_reason, * sp;
634+
635+ /* Remove spaces so that the responses of
636+ * get_reason() work in set_reason() */
637+ if((spaceless_reason = strdup(reason_str)) == NULL)
638+ return -1;
639+ while((sp = strchr(spaceless_reason, ' ') ))
640+ {
641+ memmove(sp, sp+1, strlen(sp));
642+ }
643+
644+ for (j = 0; j < NUM_REASONS; j++)
645+ {
646+ if(crl_reasons[j] && !strcasecmp(spaceless_reason, crl_reasons[j]))
647+ {
648+ reason_code = j;
649+ break;
650+ }
651+ }
652+ free(spaceless_reason);
653+ return reason_code;
654+}
655+
656+static char crypto_Revoked_set_reason_doc[] = "\n\
657+Set the reason of a Revoked object.\n\
658+\n\
659+@param reason: The reason string.\n\
660+@type reason: L{str}\n\
661+@return: None\n\
662+";
663+static PyObject *
664+crypto_Revoked_set_reason(crypto_RevokedObj *self, PyObject *args, PyObject *keywds)
665+{
666+ static char *kwlist[] = {"reason", NULL};
667+ const char *reason_str = NULL;
668+ int reason_code;
669+ ASN1_ENUMERATED *rtmp = NULL;
670+
671+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "z:set_reason",
672+ kwlist, &reason_str))
673+ return NULL;
674+
675+ if(reason_str == NULL)
676+ {
677+ delete_reason(self->revoked->extensions);
678+ goto done;
679+ }
680+
681+ reason_code = reason_str_to_code(reason_str);
682+ if(reason_code == -1)
683+ {
684+ PyErr_SetString(PyExc_ValueError, "bad reason string");
685+ return NULL;
686+ }
687+
688+ rtmp = ASN1_ENUMERATED_new();
689+ if (!rtmp || !ASN1_ENUMERATED_set(rtmp, reason_code))
690+ goto err;
691+ delete_reason(self->revoked->extensions);
692+ if (!X509_REVOKED_add1_ext_i2d(self->revoked, NID_crl_reason, rtmp, 0, 0))
693+ goto err;
694+
695+ done:
696+ Py_INCREF(Py_None);
697+ return Py_None;
698+
699+ err:
700+ exception_from_error_queue(crypto_Error);
701+ return NULL;
702+}
703+
704+
705+static char crypto_Revoked_get_reason_doc[] = "\n\
706+Return the reason of a Revoked object.\n\
707+\n\
708+@return: The reason as a string\n\
709+";
710+static PyObject *
711+crypto_Revoked_get_reason(crypto_RevokedObj *self, PyObject *args)
712+{
713+ X509_EXTENSION * ext;
714+ int j;
715+ STACK_OF(X509_EXTENSION) *sk = NULL;
716+
717+ if (!PyArg_ParseTuple(args, ":get_reason"))
718+ return NULL;
719+
720+ sk = self->revoked->extensions;
721+ for(j = 0; j < sk_X509_EXTENSION_num(sk); j++) {
722+ ext = sk_X509_EXTENSION_value(sk, j);
723+ if ( OBJ_obj2nid(ext->object) == NID_crl_reason) {
724+ return X509_EXTENSION_value_to_PyString(ext);
725+ }
726+ }
727+
728+ Py_INCREF(Py_None);
729+ return Py_None;
730+}
731+
732+
733+static char crypto_Revoked_get_rev_date_doc[] = "\n\
734+Retrieve the revocation date\n\
735+\n\
736+@return: A string giving the timestamp, in the format:\n\
737+\n\
738+ YYYYMMDDhhmmssZ\n\
739+ YYYYMMDDhhmmss+hhmm\n\
740+ YYYYMMDDhhmmss-hhmm\n\
741+";
742+
743+static PyObject*
744+crypto_Revoked_get_rev_date(crypto_RevokedObj *self, PyObject *args)
745+{
746+ /* returns a borrowed reference. */
747+ return _get_asn1_time(
748+ ":get_rev_date", self->revoked->revocationDate, args);
749+}
750+
751+static char crypto_Revoked_set_rev_date_doc[] = "\n\
752+Set the revocation timestamp\n\
753+\n\
754+@param when: A string giving the timestamp, in the format:\n\
755+\n\
756+ YYYYMMDDhhmmssZ\n\
757+ YYYYMMDDhhmmss+hhmm\n\
758+ YYYYMMDDhhmmss-hhmm\n\
759+\n\
760+@return: None\n\
761+";
762+
763+static PyObject*
764+crypto_Revoked_set_rev_date(crypto_RevokedObj *self, PyObject *args)
765+{
766+ return _set_asn1_time(
767+ "s:set_rev_date", self->revoked->revocationDate, args);
768+}
769+
770+/* The integer is converted to an upper-case hex string
771+ * without a '0x' prefix. */
772+static PyObject *
773+ASN1_INTEGER_to_PyString(ASN1_INTEGER *asn1_int)
774+{
775+ BIO *bio = NULL;
776+ PyObject *str = NULL;
777+ int str_len;
778+ char *tmp_str;
779+
780+ /* Create a openssl BIO buffer */
781+ bio = BIO_new(BIO_s_mem());
782+ if (bio == NULL)
783+ goto err;
784+
785+ /* Write the integer to the BIO as a hex string. */
786+ if(i2a_ASN1_INTEGER(bio, asn1_int) < 0)
787+ goto err;
788+
789+ /* Convert to a Python string. */
790+ str_len = BIO_get_mem_data(bio, &tmp_str);
791+ str = PyString_FromStringAndSize(tmp_str, str_len);
792+
793+ /* Cleanup */
794+ BIO_free(bio);
795+ return str;
796+
797+ err:
798+ if(bio) {
799+ BIO_free(bio);
800+ }
801+ if(str) {
802+ Py_DECREF(str);
803+ }
804+ return NULL;
805+}
806+
807+
808+static char crypto_Revoked_get_serial_doc[] = "\n\
809+Return the serial number of a Revoked structure\n\
810+\n\
811+@return: The serial number as a string\n\
812+";
813+static PyObject *
814+crypto_Revoked_get_serial(crypto_RevokedObj *self, PyObject *args)
815+{
816+ if (!PyArg_ParseTuple(args, ":get_serial"))
817+ return NULL;
818+
819+ if(self->revoked->serialNumber == NULL) {
820+ /* never happens */
821+ Py_INCREF(Py_None);
822+ return Py_None;
823+ } else {
824+ return ASN1_INTEGER_to_PyString(self->revoked->serialNumber);
825+ }
826+}
827+
828+static char crypto_Revoked_set_serial_doc[] = "\n\
829+Set the serial number of a revoked Revoked structure\n\
830+\n\
831+@param hex_str: The new serial number.\n\
832+@type hex_str: L{str}\n\
833+@return: None\n\
834+";
835+static PyObject *
836+crypto_Revoked_set_serial(crypto_RevokedObj *self, PyObject *args, PyObject *keywds)
837+{
838+ static char *kwlist[] = {"hex_str", NULL};
839+ const char *hex_str = NULL;
840+ BIGNUM *serial = NULL;
841+ ASN1_INTEGER *tmpser = NULL;
842+
843+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "s:set_serial",
844+ kwlist, &hex_str))
845+ return NULL;
846+
847+ if( ! BN_hex2bn(&serial, hex_str) ) {
848+ PyErr_SetString(PyExc_ValueError, "bad hex string");
849+ return NULL;
850+ }
851+
852+ tmpser = BN_to_ASN1_INTEGER(serial, NULL);
853+ BN_free(serial);
854+ serial = NULL;
855+ X509_REVOKED_set_serialNumber(self->revoked, tmpser);
856+ ASN1_INTEGER_free(tmpser);
857+
858+ Py_INCREF(Py_None);
859+ return Py_None;
860+}
861+
862+
863+crypto_RevokedObj *
864+crypto_Revoked_New(X509_REVOKED *revoked)
865+{
866+ crypto_RevokedObj *self;
867+
868+ self = PyObject_New(crypto_RevokedObj, &crypto_Revoked_Type);
869+ if (self==NULL)
870+ return NULL;
871+ self->revoked = revoked;
872+ return self;
873+}
874+
875+/*
876+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
877+ * { 'name', (PyCFunction)crypto_Revoked_name, METH_VARARGS, crypto_Revoked_name_doc }
878+ * for convenience
879+ */
880+#define ADD_METHOD(name) \
881+ { #name, (PyCFunction)crypto_Revoked_##name, METH_VARARGS, crypto_Revoked_##name##_doc }
882+#define ADD_KW_METHOD(name) \
883+ { #name, (PyCFunction)crypto_Revoked_##name, METH_VARARGS | METH_KEYWORDS, crypto_Revoked_##name##_doc }
884+static PyMethodDef crypto_Revoked_methods[] =
885+{
886+ ADD_METHOD(all_reasons),
887+ ADD_METHOD(get_reason),
888+ ADD_KW_METHOD(set_reason),
889+ ADD_METHOD(get_rev_date),
890+ ADD_METHOD(set_rev_date),
891+ ADD_METHOD(get_serial),
892+ ADD_KW_METHOD(set_serial),
893+ { NULL, NULL }
894+};
895+#undef ADD_METHOD
896+
897+
898+static PyObject *
899+crypto_Revoked_getattr(crypto_RevokedObj *self, char *name)
900+{
901+ return Py_FindMethod(crypto_Revoked_methods, (PyObject *)self, name);
902+}
903+
904+static void
905+crypto_Revoked_dealloc(crypto_RevokedObj *self)
906+{
907+ X509_REVOKED_free(self->revoked);
908+ self->revoked = NULL;
909+
910+ PyObject_Del(self);
911+}
912+
913+static char crypto_Revoked_doc[] = "\n\
914+Revoked() -> Revoked instance\n\
915+\n\
916+Create a new empty Revoked object.\n\
917+\n\
918+@returns: The Revoked object\n\
919+";
920+
921+static PyObject* crypto_Revoked_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) {
922+ if (!PyArg_ParseTuple(args, ":Revoked")) {
923+ return NULL;
924+ }
925+
926+ return (PyObject *)crypto_Revoked_New(X509_REVOKED_new());
927+}
928+
929+PyTypeObject crypto_Revoked_Type = {
930+ PyObject_HEAD_INIT(NULL)
931+ 0,
932+ "Revoked",
933+ sizeof(crypto_RevokedObj),
934+ 0,
935+ (destructor)crypto_Revoked_dealloc,
936+ NULL, /* print */
937+ (getattrfunc)crypto_Revoked_getattr,
938+ NULL, /* setattr */
939+ NULL, /* compare */
940+ NULL, /* repr */
941+ NULL, /* as_number */
942+ NULL, /* as_sequence */
943+ NULL, /* as_mapping */
944+ NULL, /* hash */
945+ NULL, /* call */
946+ NULL, /* str */
947+ NULL, /* getattro */
948+ NULL, /* setattro */
949+ NULL, /* as_buffer */
950+ Py_TPFLAGS_DEFAULT,
951+ crypto_Revoked_doc, /* doc */
952+ NULL, /* traverse */
953+ NULL, /* clear */
954+ NULL, /* tp_richcompare */
955+ 0, /* tp_weaklistoffset */
956+ NULL, /* tp_iter */
957+ NULL, /* tp_iternext */
958+ crypto_Revoked_methods, /* tp_methods */
959+ NULL, /* tp_members */
960+ NULL, /* tp_getset */
961+ NULL, /* tp_base */
962+ NULL, /* tp_dict */
963+ NULL, /* tp_descr_get */
964+ NULL, /* tp_descr_set */
965+ 0, /* tp_dictoffset */
966+ NULL, /* tp_init */
967+ NULL, /* tp_alloc */
968+ crypto_Revoked_new, /* tp_new */
969+};
970+
971+int init_crypto_revoked(PyObject *module) {
972+ if(PyType_Ready(&crypto_Revoked_Type) < 0) {
973+ return 0;
974+ }
975+
976+ if (PyModule_AddObject(module, "Revoked", (PyObject *)&crypto_Revoked_Type) != 0) {
977+ return 0;
978+ }
979+ return 1;
980+}
981+
982
983=== added file 'src/crypto/revoked.h'
984--- src/crypto/revoked.h 1970-01-01 00:00:00 +0000
985+++ src/crypto/revoked.h 2009-11-09 06:00:31 +0000
986@@ -0,0 +1,18 @@
987+#ifndef PyOpenSSL_crypto_REVOKED_H_
988+#define PyOpenSSL_crypto_REVOKED_H_
989+
990+#include <Python.h>
991+
992+extern PyTypeObject crypto_Revoked_Type;
993+
994+#define crypto_Revoked_Check(v) ((v)->ob_type == &crypto_Revoked_Type)
995+
996+typedef struct {
997+ PyObject_HEAD
998+ X509_REVOKED *revoked;
999+} crypto_RevokedObj;
1000+
1001+extern int init_crypto_revoked (PyObject *);
1002+extern crypto_RevokedObj * crypto_Revoked_New(X509_REVOKED *revoked);
1003+
1004+#endif
1005
1006=== modified file 'src/crypto/x509.c'
1007--- src/crypto/x509.c 2009-09-01 14:35:50 +0000
1008+++ src/crypto/x509.c 2009-11-09 06:00:31 +0000
1009@@ -335,8 +335,8 @@
1010 return Py_None;
1011 }
1012
1013-static PyObject*
1014-_set_asn1_time(char *format, ASN1_TIME* timestamp, crypto_X509Obj *self, PyObject *args)
1015+PyObject*
1016+_set_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args)
1017 {
1018 char *when;
1019
1020@@ -375,7 +375,7 @@
1021 crypto_X509_set_notBefore(crypto_X509Obj *self, PyObject *args)
1022 {
1023 return _set_asn1_time(
1024- "s:set_notBefore", X509_get_notBefore(self->x509), self, args);
1025+ "s:set_notBefore", X509_get_notBefore(self->x509), args);
1026 }
1027
1028 static char crypto_X509_set_notAfter_doc[] = "\n\
1029@@ -394,11 +394,11 @@
1030 crypto_X509_set_notAfter(crypto_X509Obj *self, PyObject *args)
1031 {
1032 return _set_asn1_time(
1033- "s:set_notAfter", X509_get_notAfter(self->x509), self, args);
1034+ "s:set_notAfter", X509_get_notAfter(self->x509), args);
1035 }
1036
1037-static PyObject*
1038-_get_asn1_time(char *format, ASN1_TIME* timestamp, crypto_X509Obj *self, PyObject *args)
1039+PyObject*
1040+_get_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args)
1041 {
1042 ASN1_GENERALIZEDTIME *gt_timestamp = NULL;
1043 PyObject *py_timestamp = NULL;
1044@@ -450,7 +450,7 @@
1045 * X509_get_notBefore returns a borrowed reference.
1046 */
1047 return _get_asn1_time(
1048- ":get_notBefore", X509_get_notBefore(self->x509), self, args);
1049+ ":get_notBefore", X509_get_notBefore(self->x509), args);
1050 }
1051
1052
1053@@ -472,7 +472,7 @@
1054 * X509_get_notAfter returns a borrowed reference.
1055 */
1056 return _get_asn1_time(
1057- ":get_notAfter", X509_get_notAfter(self->x509), self, args);
1058+ ":get_notAfter", X509_get_notAfter(self->x509), args);
1059 }
1060
1061
1062
1063=== modified file 'src/crypto/x509.h'
1064--- src/crypto/x509.h 2008-02-19 01:50:23 +0000
1065+++ src/crypto/x509.h 2009-11-09 06:00:31 +0000
1066@@ -16,8 +16,6 @@
1067 #include <Python.h>
1068 #include <openssl/ssl.h>
1069
1070-extern int init_crypto_x509 (PyObject *);
1071-
1072 extern PyTypeObject crypto_X509_Type;
1073
1074 #define crypto_X509_Check(v) ((v)->ob_type == &crypto_X509_Type)
1075@@ -28,5 +26,9 @@
1076 int dealloc;
1077 } crypto_X509Obj;
1078
1079+PyObject* _set_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args);
1080+PyObject* _get_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args);
1081+extern int init_crypto_x509 (PyObject *);
1082+
1083
1084 #endif
1085
1086=== modified file 'test/test_crypto.py'
1087--- test/test_crypto.py 2009-09-01 14:35:50 +0000
1088+++ test/test_crypto.py 2009-11-09 06:00:31 +0000
1089@@ -19,7 +19,12 @@
1090 from OpenSSL.crypto import dump_certificate, load_certificate_request
1091 from OpenSSL.crypto import dump_certificate_request, dump_privatekey
1092 from OpenSSL.crypto import PKCS7Type, load_pkcs7_data
1093+<<<<<<< TREE
1094 from OpenSSL.crypto import PKCS12Type, load_pkcs12, PKCS12
1095+=======
1096+from OpenSSL.crypto import PKCS12Type, load_pkcs12
1097+from OpenSSL.crypto import CRL, Revoked, load_crl
1098+>>>>>>> MERGE-SOURCE
1099 from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType
1100 from OpenSSL.test.util import TestCase
1101
1102@@ -1532,6 +1537,214 @@
1103 self.assertTrue(isinstance(nspki, NetscapeSPKIType))
1104
1105
1106+def _runopenssl(pem, *args):
1107+ """
1108+ Run the command line openssl tool with the given arguments and write
1109+ the given PEM to its stdin.
1110+ """
1111+ write, read = popen2(" ".join(("openssl",) + args), "b")
1112+ write.write(pem)
1113+ write.close()
1114+ return read.read()
1115+
1116+
1117+class RevokedTests(TestCase):
1118+ """
1119+ Tests for L{OpenSSL.crypto.Revoked}
1120+ """
1121+ def test_construction(self):
1122+ """
1123+ Confirm we can create L{OpenSSL.crypto.Revoked}. Check
1124+ that it is empty.
1125+ """
1126+ revoked = Revoked()
1127+ self.assertTrue( isinstance(revoked, Revoked) )
1128+ self.assertEqual( type(revoked), Revoked )
1129+ self.assertEqual( revoked.get_serial(), '00' )
1130+ self.assertEqual( revoked.get_rev_date(), None )
1131+ self.assertEqual( revoked.get_reason(), None )
1132+
1133+
1134+ def test_serial(self):
1135+ """
1136+ Confirm we can set and get serial numbers from
1137+ L{OpenSSL.crypto.Revoked}. Confirm errors are handled
1138+ with grace.
1139+ """
1140+ revoked = Revoked()
1141+ ret = revoked.set_serial('10b')
1142+ self.assertEqual( ret, None )
1143+ ser = revoked.get_serial()
1144+ self.assertEqual( ser, '010B' )
1145+
1146+ revoked.set_serial('31ppp') # a type error would be nice
1147+ ser = revoked.get_serial()
1148+ self.assertEqual( ser, '31' )
1149+
1150+ self.assertRaises(ValueError, revoked.set_serial, 'pqrst')
1151+ self.assertRaises(TypeError, revoked.set_serial, 100)
1152+
1153+
1154+ def test_date(self):
1155+ """
1156+ Confirm we can set and get revocation dates from
1157+ L{OpenSSL.crypto.Revoked}. Confirm errors are handled
1158+ with grace.
1159+ """
1160+ revoked = Revoked()
1161+ date = revoked.get_rev_date()
1162+ self.assertEqual( date, None )
1163+
1164+ now = datetime.now().strftime("%Y%m%d%H%M%SZ")
1165+ ret = revoked.set_rev_date(now)
1166+ self.assertEqual( ret, None )
1167+ date = revoked.get_rev_date()
1168+ self.assertEqual( date, now )
1169+
1170+
1171+ def test_reason(self):
1172+ """
1173+ Confirm we can set and get revocation reasons from
1174+ L{OpenSSL.crypto.Revoked}. The "get" need to work
1175+ as "set". Likewise, each reason of all_reasons() must work.
1176+ """
1177+ revoked = Revoked()
1178+ for r in revoked.all_reasons():
1179+ for x in xrange(2):
1180+ ret = revoked.set_reason(r)
1181+ self.assertEqual( ret, None )
1182+ reason = revoked.get_reason()
1183+ self.assertEqual( reason.lower().replace(' ',''),
1184+ r.lower().replace(' ','') )
1185+ r = reason # again with the resp of get
1186+
1187+ revoked.set_reason(None)
1188+ self.assertEqual(revoked.get_reason(), None)
1189+
1190+
1191+ def test_bad_reasons(self):
1192+ """
1193+ Use L{OpenSSL.crypto.Revoked.set_reason} in bad ways.
1194+ """
1195+ revoked = Revoked()
1196+ self.assertRaises(TypeError, revoked.set_reason, 100)
1197+ self.assertRaises(ValueError, revoked.set_reason, 'blue')
1198+
1199+
1200+class CRLTests(TestCase):
1201+ """
1202+ Tests for L{OpenSSL.crypto.CRL}
1203+ """
1204+ cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
1205+ pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
1206+
1207+ def test_construction(self):
1208+ """
1209+ Confirm we can create L{OpenSSL.crypto.CRL}. Check
1210+ that it is empty
1211+ """
1212+ crl = CRL()
1213+ self.assertTrue( isinstance(crl, CRL) )
1214+ self.assertEqual(crl.get_revoked(), None)
1215+
1216+
1217+ def test_export(self):
1218+ """
1219+ Use python to create a simple CRL with a revocation, and export
1220+ the CRL in formats of PEM, DER and text. Those outputs are verified
1221+ with the openssl program.
1222+ """
1223+ crl = CRL()
1224+ revoked = Revoked()
1225+ now = datetime.now().strftime("%Y%m%d%H%M%SZ")
1226+ revoked.set_rev_date(now)
1227+ revoked.set_serial('3ab')
1228+ revoked.set_reason('sUpErSeDEd')
1229+ crl.add_revoked(revoked)
1230+
1231+ # PEM format
1232+ dumped_crl = crl.export(self.cert, self.pkey, days=20)
1233+ text = _runopenssl(dumped_crl, "crl", "-noout", "-text")
1234+ text.index('Serial Number: 03AB')
1235+ text.index('Superseded')
1236+ text.index('Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=Testing Root CA')
1237+
1238+ # DER format
1239+ dumped_crl = crl.export(self.cert, self.pkey, FILETYPE_ASN1)
1240+ text = _runopenssl(dumped_crl, "crl", "-noout", "-text", "-inform", "DER")
1241+ text.index('Serial Number: 03AB')
1242+ text.index('Superseded')
1243+ text.index('Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=Testing Root CA')
1244+
1245+ # text format
1246+ dumped_text = crl.export(self.cert, self.pkey, type=FILETYPE_TEXT)
1247+ self.assertEqual(text, dumped_text)
1248+
1249+
1250+
1251+ def test_get_revoked(self):
1252+ """
1253+ Use python to create a simple CRL with two revocations.
1254+ Get back the L{Revoked} using L{OpenSSL.CRL.get_revoked} and
1255+ verify them.
1256+ """
1257+ crl = CRL()
1258+
1259+ revoked = Revoked()
1260+ now = datetime.now().strftime("%Y%m%d%H%M%SZ")
1261+ revoked.set_rev_date(now)
1262+ revoked.set_serial('3ab')
1263+ crl.add_revoked(revoked)
1264+ revoked.set_serial('100')
1265+ revoked.set_reason('sUpErSeDEd')
1266+ crl.add_revoked(revoked)
1267+
1268+ revs = crl.get_revoked()
1269+ self.assertEqual(len(revs), 2)
1270+ self.assertEqual(type(revs[0]), Revoked)
1271+ self.assertEqual(type(revs[1]), Revoked)
1272+ self.assertEqual(revs[0].get_serial(), '03AB')
1273+ self.assertEqual(revs[1].get_serial(), '0100')
1274+ self.assertEqual(revs[0].get_rev_date(), now)
1275+ self.assertEqual(revs[1].get_rev_date(), now)
1276+
1277+
1278+ def test_load_crl(self):
1279+ """
1280+ Load a known CRL and inspect its revocations. Both
1281+ PEM and DER formats are loaded.
1282+ """
1283+
1284+ crl_txt = """
1285+-----BEGIN X509 CRL-----
1286+MIIBWzCBxTANBgkqhkiG9w0BAQQFADBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMC
1287+SUwxEDAOBgNVBAcTB0NoaWNhZ28xEDAOBgNVBAoTB1Rlc3RpbmcxGDAWBgNVBAMT
1288+D1Rlc3RpbmcgUm9vdCBDQRcNMDkwNzI2MDQzNDU2WhcNMTIwOTI3MDI0MTUyWjA8
1289+MBUCAgOrGA8yMDA5MDcyNTIzMzQ1NlowIwICAQAYDzIwMDkwNzI1MjMzNDU2WjAM
1290+MAoGA1UdFQQDCgEEMA0GCSqGSIb3DQEBBAUAA4GBAEBt7xTs2htdD3d4ErrcGAw1
1291+4dKcVnIWTutoI7xxen26Wwvh8VCsT7i/UeP+rBl9rC/kfjWjzQk3/zleaarGTpBT
1292+0yp4HXRFFoRhhSE/hP+eteaPXRgrsNRLHe9ZDd69wmh7J1wMDb0m81RG7kqcbsid
1293+vrzEeLDRiiPl92dyyWmu
1294+-----END X509 CRL-----
1295+"""
1296+ crl = load_crl(FILETYPE_PEM, crl_txt)
1297+ revs = crl.get_revoked()
1298+ self.assertEqual(len(revs), 2)
1299+ self.assertEqual(revs[0].get_serial(), '03AB')
1300+ self.assertEqual(revs[0].get_reason(), None)
1301+ self.assertEqual(revs[1].get_serial(), '0100')
1302+ self.assertEqual(revs[1].get_reason(), 'Superseded')
1303+
1304+ der = _runopenssl(crl_txt, "crl", "-outform", "DER")
1305+ crl = load_crl(FILETYPE_ASN1, der)
1306+ revs = crl.get_revoked()
1307+ self.assertEqual(len(revs), 2)
1308+ self.assertEqual(revs[0].get_serial(), '03AB')
1309+ self.assertEqual(revs[0].get_reason(), None)
1310+ self.assertEqual(revs[1].get_serial(), '0100')
1311+ self.assertEqual(revs[1].get_reason(), 'Superseded')
1312+
1313+
1314
1315 if __name__ == '__main__':
1316 main()

Subscribers

People subscribed via source and target branches

to status/vote changes: