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
=== modified file 'doc/pyOpenSSL.tex'
--- doc/pyOpenSSL.tex 2009-10-23 07:51:07 +0000
+++ doc/pyOpenSSL.tex 2009-11-09 06:00:31 +0000
@@ -207,6 +207,14 @@
207method.207method.
208\end{classdesc}208\end{classdesc}
209209
210\begin{classdesc}{CRL}{}
211A class representing Certifcate Revocation List objects.
212\end{classdesc}
213
214\begin{classdesc}{Revoked}{}
215A class representing Revocation objects of CRL.
216\end{classdesc}
217
210\begin{datadesc}{FILETYPE_PEM}218\begin{datadesc}{FILETYPE_PEM}
211\dataline{FILETYPE_ASN1}219\dataline{FILETYPE_ASN1}
212File type constants.220File type constants.
@@ -259,6 +267,12 @@
259pass phrase.267pass phrase.
260\end{funcdesc}268\end{funcdesc}
261269
270\begin{funcdesc}{load_crl}{type, buffer}
271Load Certificate Revocation List (CRL) data from a string \var{buffer}.
272\var{buffer} encoded with the type \var{type}. The type \var{type}
273must either \constant{FILETYPE_PEM} or \constant{FILETYPE_ASN1}).
274\end{funcdesc}
275
262\begin{funcdesc}{load_pkcs7_data}{type, buffer}276\begin{funcdesc}{load_pkcs7_data}{type, buffer}
263Load pkcs7 data from the string \var{buffer} encoded with the type \var{type}.277Load pkcs7 data from the string \var{buffer} encoded with the type \var{type}.
264\end{funcdesc}278\end{funcdesc}
@@ -612,6 +626,60 @@
612Verify the NetscapeSPKI object using the given \var{key}.626Verify the NetscapeSPKI object using the given \var{key}.
613\end{methoddesc}627\end{methoddesc}
614628
629\subsubsection{CRL objects \label{crl}}
630
631CRL objects have the following methods:
632
633\begin{methoddesc}[CRL]{add_revoked}{revoked}
634Add a Revoked object to the CRL, by value not reference.
635\end{methoddesc}
636
637\begin{methoddesc}[CRL]{export}{cert, key\optional{, type=FILETYPE_PEM}\optional{, days=100}}
638Use \var{cert} and \var{key} to sign the CRL and return the CRL as a string.
639\var{days} is the number of days before the next CRL is due.
640\end{methoddesc}
641
642\begin{methoddesc}[CRL]{get_revoked}{}
643Return a tuple of Revoked objects, by value not reference.
644\end{methoddesc}
645
646\subsubsection{Revoked objects \label{revoked}}
647
648Revoked objects have the following methods:
649
650\begin{methoddesc}[Revoked]{all_reasons}{}
651Return a list of all supported reasons.
652\end{methoddesc}
653
654\begin{methoddesc}[Revoked]{get_reason}{}
655Return the revocation reason as a str. Can be
656None, which differs from "Unspecified".
657\end{methoddesc}
658
659\begin{methoddesc}[Revoked]{get_rev_date}{}
660Return the revocation date as a str.
661The string is formatted as an ASN1 GENERALIZEDTIME.
662\end{methoddesc}
663
664\begin{methoddesc}[Revoked]{get_serial}{}
665Return a str containing a hex number of the serial of the revoked certificate.
666\end{methoddesc}
667
668\begin{methoddesc}[Revoked]{set_reason}{reason}
669Set the revocation reason. \var{reason} must
670be None or a string, but the values are limited.
671Spaces and case are ignored. See \method{all_reasons}.
672\end{methoddesc}
673
674\begin{methoddesc}[Revoked]{set_rev_date}{date}
675Set the revocation date.
676The string is formatted as an ASN1 GENERALIZEDTIME.
677\end{methoddesc}
678
679\begin{methoddesc}[Revoked]{set_serial}{serial}
680\var{serial} is a string containing a hex number of the serial of the revoked certificate.
681\end{methoddesc}
682
615683
616% % % rand module684% % % rand module
617685
618686
=== modified file 'setup.py'
--- setup.py 2009-07-24 13:37:38 +0000
+++ setup.py 2009-11-09 06:00:30 +0000
@@ -23,12 +23,14 @@
23 'src/crypto/x509store.c', 'src/crypto/x509req.c',23 'src/crypto/x509store.c', 'src/crypto/x509req.c',
24 'src/crypto/x509ext.c', 'src/crypto/pkcs7.c',24 'src/crypto/x509ext.c', 'src/crypto/pkcs7.c',
25 'src/crypto/pkcs12.c', 'src/crypto/netscape_spki.c',25 'src/crypto/pkcs12.c', 'src/crypto/netscape_spki.c',
26 'src/crypto/revoked.c', 'src/crypto/crl.c',
26 'src/util.c']27 'src/util.c']
27crypto_dep = ['src/crypto/crypto.h', 'src/crypto/x509.h',28crypto_dep = ['src/crypto/crypto.h', 'src/crypto/x509.h',
28 'src/crypto/x509name.h', 'src/crypto/pkey.h',29 'src/crypto/x509name.h', 'src/crypto/pkey.h',
29 'src/crypto/x509store.h', 'src/crypto/x509req.h',30 'src/crypto/x509store.h', 'src/crypto/x509req.h',
30 'src/crypto/x509ext.h', 'src/crypto/pkcs7.h',31 'src/crypto/x509ext.h', 'src/crypto/pkcs7.h',
31 'src/crypto/pkcs12.h', 'src/crypto/netscape_spki.h',32 'src/crypto/pkcs12.h', 'src/crypto/netscape_spki.h',
33 'src/crypto/revoked.h', 'src/crypto/crl.h',
32 'src/util.h']34 'src/util.h']
33rand_src = ['src/rand/rand.c', 'src/util.c']35rand_src = ['src/rand/rand.c', 'src/util.c']
34rand_dep = ['src/util.h']36rand_dep = ['src/util.h']
3537
=== added file 'src/crypto/crl.c'
--- src/crypto/crl.c 1970-01-01 00:00:00 +0000
+++ src/crypto/crl.c 2009-11-09 06:00:31 +0000
@@ -0,0 +1,294 @@
1#include <Python.h>
2#define crypto_MODULE
3#include "crypto.h"
4
5
6static X509_REVOKED * X509_REVOKED_dup(X509_REVOKED *orig)
7{
8 X509_REVOKED *dupe = NULL;
9
10 dupe = X509_REVOKED_new();
11 if(dupe == NULL)
12 return NULL;
13 if(orig->serialNumber)
14 {
15 dupe->serialNumber = M_ASN1_INTEGER_dup(orig->serialNumber);
16 }
17 if(orig->revocationDate)
18 {
19 dupe->revocationDate = M_ASN1_INTEGER_dup(orig->revocationDate);
20 }
21 if(orig->extensions)
22 {
23 STACK_OF(X509_EXTENSION) *sk = NULL;
24 X509_EXTENSION * ext;
25 int j;
26
27 sk = sk_X509_EXTENSION_new_null();
28 for(j = 0; j < sk_X509_EXTENSION_num(orig->extensions); j++) {
29 ext = sk_X509_EXTENSION_value(orig->extensions, j);
30 ext = X509_EXTENSION_dup(ext);
31 sk_X509_EXTENSION_push(sk, ext);
32 }
33 dupe->extensions = sk;
34 }
35 dupe->sequence = orig->sequence;
36 return dupe;
37}
38
39static char crypto_CRL_get_revoked_doc[] = "\n\
40Return revoked portion of the CRL structure (by value\n\
41not reference).\n\
42\n\
43@return: A tuple of Revoked objects.\n\
44";
45static PyObject *
46crypto_CRL_get_revoked(crypto_CRLObj *self, PyObject *args)
47{
48 int j, num_rev;
49 X509_REVOKED *r = NULL;
50 PyObject *obj = NULL, *rev_obj;
51
52 if (!PyArg_ParseTuple(args, ":get_revoked"))
53 return NULL;
54
55 num_rev = sk_X509_REVOKED_num(self->crl->crl->revoked);
56 if(num_rev < 0)
57 {
58 Py_INCREF(Py_None);
59 return Py_None;
60 }
61 if ((obj = PyTuple_New(num_rev)) == NULL)
62 return NULL;
63
64 for(j = 0; j < num_rev; j++)
65 {
66 r = sk_X509_REVOKED_value(self->crl->crl->revoked, j);
67 r = X509_REVOKED_dup(r);
68 if( r == NULL )
69 goto error;
70 rev_obj = (PyObject *) crypto_Revoked_New(r);
71 if( rev_obj == NULL )
72 goto error;
73 r = NULL; /* it's now owned by rev_obj */
74 PyTuple_SET_ITEM(obj, j, rev_obj);
75 }
76 return obj;
77
78 error:
79 if(r)
80 X509_REVOKED_free(r);
81 Py_XDECREF(obj);
82 return NULL;
83}
84
85static char crypto_CRL_add_revoked_doc[] = "\n\
86Add a revoked (by value not reference) to the CRL structure\n\
87\n\
88@param cert: The new revoked.\n\
89@type cert: L{X509}\n\
90@return: None\n\
91";
92static PyObject *
93crypto_CRL_add_revoked(crypto_CRLObj *self, PyObject *args, PyObject *keywds)
94{
95 crypto_RevokedObj * rev_obj = NULL;
96 static char *kwlist[] = {"revoked", NULL};
97 X509_REVOKED * dup;
98
99 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!:add_revoked",
100 kwlist, &crypto_Revoked_Type, &rev_obj))
101 return NULL;
102
103 dup = X509_REVOKED_dup( rev_obj->revoked );
104 if(dup == NULL)
105 return NULL;
106 X509_CRL_add0_revoked(self->crl, dup);
107
108 Py_INCREF(Py_None);
109 return Py_None;
110}
111
112static char crypto_CRL_export_doc[] = "\n\
113Export a CRL as a string\n\
114\n\
115@param cert: Used to sign CRL.\n\
116@type cert: L{X509}\n\
117@param key: Used to sign CRL.\n\
118@type key: L{PKey}\n\
119@return: None\n\
120";
121static PyObject *
122crypto_CRL_export(crypto_CRLObj *self, PyObject *args, PyObject *keywds)
123{
124 int ret, buf_len, type = X509_FILETYPE_PEM, days = 100;
125 char *temp;
126 BIO *bio;
127 PyObject *buffer;
128 crypto_PKeyObj *key;
129 ASN1_TIME *tmptm;
130 crypto_X509Obj *x509;
131 static char *kwlist[] = {"cert", "key", "type", "days", NULL};
132
133 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!O!|ii:dump_crl", kwlist,
134 &crypto_X509_Type, &x509,
135 &crypto_PKey_Type, &key, &type, &days))
136 return NULL;
137
138 bio=BIO_new(BIO_s_mem());
139 tmptm = ASN1_TIME_new();
140 if (!tmptm)
141 return 0;
142 X509_gmtime_adj(tmptm,0);
143 X509_CRL_set_lastUpdate(self->crl, tmptm);
144 X509_gmtime_adj(tmptm,days*24*60*60);
145 X509_CRL_set_nextUpdate(self->crl, tmptm);
146 ASN1_TIME_free(tmptm);
147 X509_CRL_set_issuer_name(self->crl, X509_get_subject_name(x509->x509));
148 X509_CRL_sign(self->crl, key->pkey, EVP_md5());
149 switch (type)
150 {
151 case X509_FILETYPE_PEM:
152 ret = PEM_write_bio_X509_CRL(bio, self->crl);
153 break;
154
155 case X509_FILETYPE_ASN1:
156 ret = (int) i2d_X509_CRL_bio(bio, self->crl);
157 break;
158
159 case X509_FILETYPE_TEXT:
160 ret = X509_CRL_print(bio, self->crl);
161 break;
162
163 default:
164 PyErr_SetString(PyExc_ValueError,
165 "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT");
166 return NULL;
167 };
168 if( ! ret )
169 {
170 exception_from_error_queue(crypto_Error);
171 BIO_free(bio);
172 return NULL;
173 }
174 buf_len = BIO_get_mem_data(bio, &temp);
175 buffer = PyString_FromStringAndSize(temp, buf_len);
176 BIO_free(bio);
177 return buffer;
178}
179
180crypto_CRLObj *
181crypto_CRL_New(X509_CRL *crl)
182{
183 crypto_CRLObj *self;
184
185 self = PyObject_New(crypto_CRLObj, &crypto_CRL_Type);
186 if (self==NULL)
187 return NULL;
188 self->crl = crl;
189 return self;
190}
191
192/*
193 * ADD_METHOD(name) expands to a correct PyMethodDef declaration
194 * { 'name', (PyCFunction)crypto_CRL_name, METH_VARARGS, crypto_CRL_name_doc }
195 * for convenience
196 */
197#define ADD_METHOD(name) \
198 { #name, (PyCFunction)crypto_CRL_##name, METH_VARARGS, crypto_CRL_##name##_doc }
199#define ADD_KW_METHOD(name) \
200 { #name, (PyCFunction)crypto_CRL_##name, METH_VARARGS | METH_KEYWORDS, crypto_CRL_##name##_doc }
201static PyMethodDef crypto_CRL_methods[] =
202{
203 ADD_KW_METHOD(add_revoked),
204 ADD_METHOD(get_revoked),
205 ADD_KW_METHOD(export),
206 { NULL, NULL }
207};
208#undef ADD_METHOD
209
210
211static PyObject *
212crypto_CRL_getattr(crypto_CRLObj *self, char *name)
213{
214 return Py_FindMethod(crypto_CRL_methods, (PyObject *)self, name);
215}
216
217static void
218crypto_CRL_dealloc(crypto_CRLObj *self)
219{
220 X509_CRL_free(self->crl);
221 self->crl = NULL;
222
223 PyObject_Del(self);
224}
225
226static char crypto_CRL_doc[] = "\n\
227CRL() -> CRL instance\n\
228\n\
229Create a new empty CRL object.\n\
230\n\
231@returns: The CRL object\n\
232";
233
234static PyObject* crypto_CRL_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) {
235 if (!PyArg_ParseTuple(args, ":CRL")) {
236 return NULL;
237 }
238
239 return (PyObject *)crypto_CRL_New(X509_CRL_new());
240}
241
242PyTypeObject crypto_CRL_Type = {
243 PyObject_HEAD_INIT(NULL)
244 0,
245 "CRL",
246 sizeof(crypto_CRLObj),
247 0,
248 (destructor)crypto_CRL_dealloc,
249 NULL, /* print */
250 (getattrfunc)crypto_CRL_getattr,
251 NULL, /* setattr */
252 NULL, /* compare */
253 NULL, /* repr */
254 NULL, /* as_number */
255 NULL, /* as_sequence */
256 NULL, /* as_mapping */
257 NULL, /* hash */
258 NULL, /* call */
259 NULL, /* str */
260 NULL, /* getattro */
261 NULL, /* setattro */
262 NULL, /* as_buffer */
263 Py_TPFLAGS_DEFAULT,
264 crypto_CRL_doc, /* doc */
265 NULL, /* traverse */
266 NULL, /* clear */
267 NULL, /* tp_richcompare */
268 0, /* tp_weaklistoffset */
269 NULL, /* tp_iter */
270 NULL, /* tp_iternext */
271 crypto_CRL_methods, /* tp_methods */
272 NULL, /* tp_members */
273 NULL, /* tp_getset */
274 NULL, /* tp_base */
275 NULL, /* tp_dict */
276 NULL, /* tp_descr_get */
277 NULL, /* tp_descr_set */
278 0, /* tp_dictoffset */
279 NULL, /* tp_init */
280 NULL, /* tp_alloc */
281 crypto_CRL_new, /* tp_new */
282};
283
284int init_crypto_crl(PyObject *module) {
285 if(PyType_Ready(&crypto_CRL_Type) < 0) {
286 return 0;
287 }
288
289 if (PyModule_AddObject(module, "CRL", (PyObject *)&crypto_CRL_Type) != 0) {
290 return 0;
291 }
292 return 1;
293}
294
0295
=== added file 'src/crypto/crl.h'
--- src/crypto/crl.h 1970-01-01 00:00:00 +0000
+++ src/crypto/crl.h 2009-11-09 06:00:31 +0000
@@ -0,0 +1,19 @@
1#ifndef PyOpenSSL_crypto_CRL_H_
2#define PyOpenSSL_crypto_CRL_H_
3
4#include <Python.h>
5
6extern int init_crypto_crl (PyObject *);
7
8extern PyTypeObject crypto_CRL_Type;
9
10#define crypto_CRL_Check(v) ((v)->ob_type == &crypto_CRL_Type)
11
12typedef struct {
13 PyObject_HEAD
14 X509_CRL *crl;
15} crypto_CRLObj;
16
17crypto_CRLObj * crypto_CRL_New(X509_CRL *crl);
18
19#endif
020
=== modified file 'src/crypto/crypto.c'
--- src/crypto/crypto.c 2009-07-17 17:50:12 +0000
+++ src/crypto/crypto.c 2009-11-09 06:00:31 +0000
@@ -431,6 +431,53 @@
431 return buffer;431 return buffer;
432}432}
433433
434static char crypto_load_crl_doc[] = "\n\
435Load a certificate revocation list from a buffer\n\
436\n\
437@param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\
438@param buffer: The buffer the CRL is stored in\n\
439\n\
440@return: The PKey object\n\
441";
442
443static PyObject *
444crypto_load_crl(PyObject *spam, PyObject *args)
445{
446 int type, len;
447 char *buffer;
448 BIO *bio;
449 X509_CRL *crl;
450
451 if (!PyArg_ParseTuple(args, "is#:load_crl", &type, &buffer, &len))
452 return NULL;
453
454 bio = BIO_new_mem_buf(buffer, len);
455 switch (type)
456 {
457 case X509_FILETYPE_PEM:
458 crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL);
459 break;
460
461 case X509_FILETYPE_ASN1:
462 crl = d2i_X509_CRL_bio(bio, NULL);
463 break;
464
465 default:
466 PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
467 BIO_free(bio);
468 return NULL;
469 }
470 BIO_free(bio);
471
472 if (crl == NULL)
473 {
474 exception_from_error_queue(crypto_Error);
475 return NULL;
476 }
477
478 return (PyObject *)crypto_CRL_New(crl);
479}
480
434static char crypto_load_pkcs7_data_doc[] = "\n\481static char crypto_load_pkcs7_data_doc[] = "\n\
435Load pkcs7 data from a buffer\n\482Load pkcs7 data from a buffer\n\
436\n\483\n\
@@ -555,6 +602,7 @@
555 { "dump_certificate", (PyCFunction)crypto_dump_certificate, METH_VARARGS, crypto_dump_certificate_doc },602 { "dump_certificate", (PyCFunction)crypto_dump_certificate, METH_VARARGS, crypto_dump_certificate_doc },
556 { "load_certificate_request", (PyCFunction)crypto_load_certificate_request, METH_VARARGS, crypto_load_certificate_request_doc },603 { "load_certificate_request", (PyCFunction)crypto_load_certificate_request, METH_VARARGS, crypto_load_certificate_request_doc },
557 { "dump_certificate_request", (PyCFunction)crypto_dump_certificate_request, METH_VARARGS, crypto_dump_certificate_request_doc },604 { "dump_certificate_request", (PyCFunction)crypto_dump_certificate_request, METH_VARARGS, crypto_dump_certificate_request_doc },
605 { "load_crl", (PyCFunction)crypto_load_crl, METH_VARARGS, crypto_load_crl_doc },
558 { "load_pkcs7_data", (PyCFunction)crypto_load_pkcs7_data, METH_VARARGS, crypto_load_pkcs7_data_doc },606 { "load_pkcs7_data", (PyCFunction)crypto_load_pkcs7_data, METH_VARARGS, crypto_load_pkcs7_data_doc },
559 { "load_pkcs12", (PyCFunction)crypto_load_pkcs12, METH_VARARGS, crypto_load_pkcs12_doc },607 { "load_pkcs12", (PyCFunction)crypto_load_pkcs12, METH_VARARGS, crypto_load_pkcs12_doc },
560 { "X509_verify_cert_error_string", (PyCFunction)crypto_X509_verify_cert_error_string, METH_VARARGS, crypto_X509_verify_cert_error_string_doc },608 { "X509_verify_cert_error_string", (PyCFunction)crypto_X509_verify_cert_error_string, METH_VARARGS, crypto_X509_verify_cert_error_string_doc },
@@ -696,7 +744,10 @@
696 goto error;744 goto error;
697 if (!init_crypto_netscape_spki(module))745 if (!init_crypto_netscape_spki(module))
698 goto error;746 goto error;
699747 if (!init_crypto_crl(module))
748 goto error;
749 if (!init_crypto_revoked(module))
750 goto error;
700error:751error:
701 ;752 ;
702}753}
703754
=== modified file 'src/crypto/crypto.h'
--- src/crypto/crypto.h 2009-07-17 20:06:12 +0000
+++ src/crypto/crypto.h 2009-11-09 06:00:31 +0000
@@ -23,6 +23,8 @@
23#include "x509ext.h"23#include "x509ext.h"
24#include "pkcs7.h"24#include "pkcs7.h"
25#include "pkcs12.h"25#include "pkcs12.h"
26#include "crl.h"
27#include "revoked.h"
26#include "../util.h"28#include "../util.h"
2729
28extern PyObject *crypto_Error;30extern PyObject *crypto_Error;
2931
=== added file 'src/crypto/revoked.c'
--- src/crypto/revoked.c 1970-01-01 00:00:00 +0000
+++ src/crypto/revoked.c 2009-11-09 06:00:31 +0000
@@ -0,0 +1,451 @@
1#include <Python.h>
2#define crypto_MODULE
3#include "crypto.h"
4
5
6/* http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_ */
7/* which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches */
8/* OCSP_crl_reason_str. We use the latter, just like the command line program. */
9static const char *crl_reasons[] = {
10 "unspecified",
11 "keyCompromise",
12 "CACompromise",
13 "affiliationChanged",
14 "superseded",
15 "cessationOfOperation",
16 "certificateHold",
17 NULL,
18 "removeFromCRL",
19};
20
21#define NUM_REASONS (sizeof(crl_reasons) / sizeof(char *))
22
23static char crypto_Revoked_all_reasons_doc[] = "\n\
24Return a list of all the supported reason strings.\n\
25\n\
26@return: A list of reason strings.\n\
27";
28static PyObject *
29crypto_Revoked_all_reasons(crypto_RevokedObj *self, PyObject *args)
30{
31 PyObject *list, *str;
32 int j;
33
34 list = PyList_New(0);
35 for (j = 0; j < NUM_REASONS; j++)
36 {
37 if( crl_reasons[j] )
38 {
39 str = PyString_FromString(crl_reasons[j]);
40 PyList_Append(list, str);
41 Py_DECREF(str);
42 }
43 }
44 return list;
45}
46
47static PyObject *
48X509_EXTENSION_value_to_PyString(X509_EXTENSION *ex)
49{
50 BIO *bio = NULL;
51 PyObject *str = NULL;
52 int str_len;
53 char *tmp_str;
54
55 /* Create a openssl BIO buffer */
56 bio = BIO_new(BIO_s_mem());
57 if (bio == NULL)
58 goto err;
59
60 /* These are not the droids you are looking for. */
61 if(!X509V3_EXT_print(bio, ex, 0, 0))
62 if(M_ASN1_OCTET_STRING_print(bio, ex->value) == 0)
63 goto err;
64
65 /* Convert to a Python string. */
66 str_len = BIO_get_mem_data(bio, &tmp_str);
67 str = PyString_FromStringAndSize(tmp_str, str_len);
68
69 /* Cleanup */
70 BIO_free(bio);
71 return str;
72
73 err:
74 if(bio) {
75 BIO_free(bio);
76 }
77 if(str) {
78 Py_DECREF(str);
79 }
80 return NULL;
81}
82
83static void
84delete_reason(STACK_OF(X509_EXTENSION) *sk)
85{
86 X509_EXTENSION * ext;
87 int j;
88
89 for(j = 0; j < sk_X509_EXTENSION_num(sk); j++) {
90 ext = sk_X509_EXTENSION_value(sk, j);
91 if ( OBJ_obj2nid(ext->object) == NID_crl_reason) {
92 X509_EXTENSION_free(ext);
93 (void) sk_X509_EXTENSION_delete(sk, j);
94 break;
95 }
96 }
97}
98
99static int
100reason_str_to_code(const char * reason_str)
101{
102 int reason_code = -1, j;
103 char *spaceless_reason, * sp;
104
105 /* Remove spaces so that the responses of
106 * get_reason() work in set_reason() */
107 if((spaceless_reason = strdup(reason_str)) == NULL)
108 return -1;
109 while((sp = strchr(spaceless_reason, ' ') ))
110 {
111 memmove(sp, sp+1, strlen(sp));
112 }
113
114 for (j = 0; j < NUM_REASONS; j++)
115 {
116 if(crl_reasons[j] && !strcasecmp(spaceless_reason, crl_reasons[j]))
117 {
118 reason_code = j;
119 break;
120 }
121 }
122 free(spaceless_reason);
123 return reason_code;
124}
125
126static char crypto_Revoked_set_reason_doc[] = "\n\
127Set the reason of a Revoked object.\n\
128\n\
129@param reason: The reason string.\n\
130@type reason: L{str}\n\
131@return: None\n\
132";
133static PyObject *
134crypto_Revoked_set_reason(crypto_RevokedObj *self, PyObject *args, PyObject *keywds)
135{
136 static char *kwlist[] = {"reason", NULL};
137 const char *reason_str = NULL;
138 int reason_code;
139 ASN1_ENUMERATED *rtmp = NULL;
140
141 if (!PyArg_ParseTupleAndKeywords(args, keywds, "z:set_reason",
142 kwlist, &reason_str))
143 return NULL;
144
145 if(reason_str == NULL)
146 {
147 delete_reason(self->revoked->extensions);
148 goto done;
149 }
150
151 reason_code = reason_str_to_code(reason_str);
152 if(reason_code == -1)
153 {
154 PyErr_SetString(PyExc_ValueError, "bad reason string");
155 return NULL;
156 }
157
158 rtmp = ASN1_ENUMERATED_new();
159 if (!rtmp || !ASN1_ENUMERATED_set(rtmp, reason_code))
160 goto err;
161 delete_reason(self->revoked->extensions);
162 if (!X509_REVOKED_add1_ext_i2d(self->revoked, NID_crl_reason, rtmp, 0, 0))
163 goto err;
164
165 done:
166 Py_INCREF(Py_None);
167 return Py_None;
168
169 err:
170 exception_from_error_queue(crypto_Error);
171 return NULL;
172}
173
174
175static char crypto_Revoked_get_reason_doc[] = "\n\
176Return the reason of a Revoked object.\n\
177\n\
178@return: The reason as a string\n\
179";
180static PyObject *
181crypto_Revoked_get_reason(crypto_RevokedObj *self, PyObject *args)
182{
183 X509_EXTENSION * ext;
184 int j;
185 STACK_OF(X509_EXTENSION) *sk = NULL;
186
187 if (!PyArg_ParseTuple(args, ":get_reason"))
188 return NULL;
189
190 sk = self->revoked->extensions;
191 for(j = 0; j < sk_X509_EXTENSION_num(sk); j++) {
192 ext = sk_X509_EXTENSION_value(sk, j);
193 if ( OBJ_obj2nid(ext->object) == NID_crl_reason) {
194 return X509_EXTENSION_value_to_PyString(ext);
195 }
196 }
197
198 Py_INCREF(Py_None);
199 return Py_None;
200}
201
202
203static char crypto_Revoked_get_rev_date_doc[] = "\n\
204Retrieve the revocation date\n\
205\n\
206@return: A string giving the timestamp, in the format:\n\
207\n\
208 YYYYMMDDhhmmssZ\n\
209 YYYYMMDDhhmmss+hhmm\n\
210 YYYYMMDDhhmmss-hhmm\n\
211";
212
213static PyObject*
214crypto_Revoked_get_rev_date(crypto_RevokedObj *self, PyObject *args)
215{
216 /* returns a borrowed reference. */
217 return _get_asn1_time(
218 ":get_rev_date", self->revoked->revocationDate, args);
219}
220
221static char crypto_Revoked_set_rev_date_doc[] = "\n\
222Set the revocation timestamp\n\
223\n\
224@param when: A string giving the timestamp, in the format:\n\
225\n\
226 YYYYMMDDhhmmssZ\n\
227 YYYYMMDDhhmmss+hhmm\n\
228 YYYYMMDDhhmmss-hhmm\n\
229\n\
230@return: None\n\
231";
232
233static PyObject*
234crypto_Revoked_set_rev_date(crypto_RevokedObj *self, PyObject *args)
235{
236 return _set_asn1_time(
237 "s:set_rev_date", self->revoked->revocationDate, args);
238}
239
240/* The integer is converted to an upper-case hex string
241 * without a '0x' prefix. */
242static PyObject *
243ASN1_INTEGER_to_PyString(ASN1_INTEGER *asn1_int)
244{
245 BIO *bio = NULL;
246 PyObject *str = NULL;
247 int str_len;
248 char *tmp_str;
249
250 /* Create a openssl BIO buffer */
251 bio = BIO_new(BIO_s_mem());
252 if (bio == NULL)
253 goto err;
254
255 /* Write the integer to the BIO as a hex string. */
256 if(i2a_ASN1_INTEGER(bio, asn1_int) < 0)
257 goto err;
258
259 /* Convert to a Python string. */
260 str_len = BIO_get_mem_data(bio, &tmp_str);
261 str = PyString_FromStringAndSize(tmp_str, str_len);
262
263 /* Cleanup */
264 BIO_free(bio);
265 return str;
266
267 err:
268 if(bio) {
269 BIO_free(bio);
270 }
271 if(str) {
272 Py_DECREF(str);
273 }
274 return NULL;
275}
276
277
278static char crypto_Revoked_get_serial_doc[] = "\n\
279Return the serial number of a Revoked structure\n\
280\n\
281@return: The serial number as a string\n\
282";
283static PyObject *
284crypto_Revoked_get_serial(crypto_RevokedObj *self, PyObject *args)
285{
286 if (!PyArg_ParseTuple(args, ":get_serial"))
287 return NULL;
288
289 if(self->revoked->serialNumber == NULL) {
290 /* never happens */
291 Py_INCREF(Py_None);
292 return Py_None;
293 } else {
294 return ASN1_INTEGER_to_PyString(self->revoked->serialNumber);
295 }
296}
297
298static char crypto_Revoked_set_serial_doc[] = "\n\
299Set the serial number of a revoked Revoked structure\n\
300\n\
301@param hex_str: The new serial number.\n\
302@type hex_str: L{str}\n\
303@return: None\n\
304";
305static PyObject *
306crypto_Revoked_set_serial(crypto_RevokedObj *self, PyObject *args, PyObject *keywds)
307{
308 static char *kwlist[] = {"hex_str", NULL};
309 const char *hex_str = NULL;
310 BIGNUM *serial = NULL;
311 ASN1_INTEGER *tmpser = NULL;
312
313 if (!PyArg_ParseTupleAndKeywords(args, keywds, "s:set_serial",
314 kwlist, &hex_str))
315 return NULL;
316
317 if( ! BN_hex2bn(&serial, hex_str) ) {
318 PyErr_SetString(PyExc_ValueError, "bad hex string");
319 return NULL;
320 }
321
322 tmpser = BN_to_ASN1_INTEGER(serial, NULL);
323 BN_free(serial);
324 serial = NULL;
325 X509_REVOKED_set_serialNumber(self->revoked, tmpser);
326 ASN1_INTEGER_free(tmpser);
327
328 Py_INCREF(Py_None);
329 return Py_None;
330}
331
332
333crypto_RevokedObj *
334crypto_Revoked_New(X509_REVOKED *revoked)
335{
336 crypto_RevokedObj *self;
337
338 self = PyObject_New(crypto_RevokedObj, &crypto_Revoked_Type);
339 if (self==NULL)
340 return NULL;
341 self->revoked = revoked;
342 return self;
343}
344
345/*
346 * ADD_METHOD(name) expands to a correct PyMethodDef declaration
347 * { 'name', (PyCFunction)crypto_Revoked_name, METH_VARARGS, crypto_Revoked_name_doc }
348 * for convenience
349 */
350#define ADD_METHOD(name) \
351 { #name, (PyCFunction)crypto_Revoked_##name, METH_VARARGS, crypto_Revoked_##name##_doc }
352#define ADD_KW_METHOD(name) \
353 { #name, (PyCFunction)crypto_Revoked_##name, METH_VARARGS | METH_KEYWORDS, crypto_Revoked_##name##_doc }
354static PyMethodDef crypto_Revoked_methods[] =
355{
356 ADD_METHOD(all_reasons),
357 ADD_METHOD(get_reason),
358 ADD_KW_METHOD(set_reason),
359 ADD_METHOD(get_rev_date),
360 ADD_METHOD(set_rev_date),
361 ADD_METHOD(get_serial),
362 ADD_KW_METHOD(set_serial),
363 { NULL, NULL }
364};
365#undef ADD_METHOD
366
367
368static PyObject *
369crypto_Revoked_getattr(crypto_RevokedObj *self, char *name)
370{
371 return Py_FindMethod(crypto_Revoked_methods, (PyObject *)self, name);
372}
373
374static void
375crypto_Revoked_dealloc(crypto_RevokedObj *self)
376{
377 X509_REVOKED_free(self->revoked);
378 self->revoked = NULL;
379
380 PyObject_Del(self);
381}
382
383static char crypto_Revoked_doc[] = "\n\
384Revoked() -> Revoked instance\n\
385\n\
386Create a new empty Revoked object.\n\
387\n\
388@returns: The Revoked object\n\
389";
390
391static PyObject* crypto_Revoked_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) {
392 if (!PyArg_ParseTuple(args, ":Revoked")) {
393 return NULL;
394 }
395
396 return (PyObject *)crypto_Revoked_New(X509_REVOKED_new());
397}
398
399PyTypeObject crypto_Revoked_Type = {
400 PyObject_HEAD_INIT(NULL)
401 0,
402 "Revoked",
403 sizeof(crypto_RevokedObj),
404 0,
405 (destructor)crypto_Revoked_dealloc,
406 NULL, /* print */
407 (getattrfunc)crypto_Revoked_getattr,
408 NULL, /* setattr */
409 NULL, /* compare */
410 NULL, /* repr */
411 NULL, /* as_number */
412 NULL, /* as_sequence */
413 NULL, /* as_mapping */
414 NULL, /* hash */
415 NULL, /* call */
416 NULL, /* str */
417 NULL, /* getattro */
418 NULL, /* setattro */
419 NULL, /* as_buffer */
420 Py_TPFLAGS_DEFAULT,
421 crypto_Revoked_doc, /* doc */
422 NULL, /* traverse */
423 NULL, /* clear */
424 NULL, /* tp_richcompare */
425 0, /* tp_weaklistoffset */
426 NULL, /* tp_iter */
427 NULL, /* tp_iternext */
428 crypto_Revoked_methods, /* tp_methods */
429 NULL, /* tp_members */
430 NULL, /* tp_getset */
431 NULL, /* tp_base */
432 NULL, /* tp_dict */
433 NULL, /* tp_descr_get */
434 NULL, /* tp_descr_set */
435 0, /* tp_dictoffset */
436 NULL, /* tp_init */
437 NULL, /* tp_alloc */
438 crypto_Revoked_new, /* tp_new */
439};
440
441int init_crypto_revoked(PyObject *module) {
442 if(PyType_Ready(&crypto_Revoked_Type) < 0) {
443 return 0;
444 }
445
446 if (PyModule_AddObject(module, "Revoked", (PyObject *)&crypto_Revoked_Type) != 0) {
447 return 0;
448 }
449 return 1;
450}
451
0452
=== added file 'src/crypto/revoked.h'
--- src/crypto/revoked.h 1970-01-01 00:00:00 +0000
+++ src/crypto/revoked.h 2009-11-09 06:00:31 +0000
@@ -0,0 +1,18 @@
1#ifndef PyOpenSSL_crypto_REVOKED_H_
2#define PyOpenSSL_crypto_REVOKED_H_
3
4#include <Python.h>
5
6extern PyTypeObject crypto_Revoked_Type;
7
8#define crypto_Revoked_Check(v) ((v)->ob_type == &crypto_Revoked_Type)
9
10typedef struct {
11 PyObject_HEAD
12 X509_REVOKED *revoked;
13} crypto_RevokedObj;
14
15extern int init_crypto_revoked (PyObject *);
16extern crypto_RevokedObj * crypto_Revoked_New(X509_REVOKED *revoked);
17
18#endif
019
=== modified file 'src/crypto/x509.c'
--- src/crypto/x509.c 2009-09-01 14:35:50 +0000
+++ src/crypto/x509.c 2009-11-09 06:00:31 +0000
@@ -335,8 +335,8 @@
335 return Py_None;335 return Py_None;
336}336}
337337
338static PyObject*338PyObject*
339_set_asn1_time(char *format, ASN1_TIME* timestamp, crypto_X509Obj *self, PyObject *args)339_set_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args)
340{340{
341 char *when;341 char *when;
342342
@@ -375,7 +375,7 @@
375crypto_X509_set_notBefore(crypto_X509Obj *self, PyObject *args)375crypto_X509_set_notBefore(crypto_X509Obj *self, PyObject *args)
376{376{
377 return _set_asn1_time(377 return _set_asn1_time(
378 "s:set_notBefore", X509_get_notBefore(self->x509), self, args);378 "s:set_notBefore", X509_get_notBefore(self->x509), args);
379}379}
380380
381static char crypto_X509_set_notAfter_doc[] = "\n\381static char crypto_X509_set_notAfter_doc[] = "\n\
@@ -394,11 +394,11 @@
394crypto_X509_set_notAfter(crypto_X509Obj *self, PyObject *args)394crypto_X509_set_notAfter(crypto_X509Obj *self, PyObject *args)
395{395{
396 return _set_asn1_time(396 return _set_asn1_time(
397 "s:set_notAfter", X509_get_notAfter(self->x509), self, args);397 "s:set_notAfter", X509_get_notAfter(self->x509), args);
398}398}
399399
400static PyObject*400PyObject*
401_get_asn1_time(char *format, ASN1_TIME* timestamp, crypto_X509Obj *self, PyObject *args)401_get_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args)
402{402{
403 ASN1_GENERALIZEDTIME *gt_timestamp = NULL;403 ASN1_GENERALIZEDTIME *gt_timestamp = NULL;
404 PyObject *py_timestamp = NULL;404 PyObject *py_timestamp = NULL;
@@ -450,7 +450,7 @@
450 * X509_get_notBefore returns a borrowed reference.450 * X509_get_notBefore returns a borrowed reference.
451 */451 */
452 return _get_asn1_time(452 return _get_asn1_time(
453 ":get_notBefore", X509_get_notBefore(self->x509), self, args);453 ":get_notBefore", X509_get_notBefore(self->x509), args);
454}454}
455455
456456
@@ -472,7 +472,7 @@
472 * X509_get_notAfter returns a borrowed reference.472 * X509_get_notAfter returns a borrowed reference.
473 */473 */
474 return _get_asn1_time(474 return _get_asn1_time(
475 ":get_notAfter", X509_get_notAfter(self->x509), self, args);475 ":get_notAfter", X509_get_notAfter(self->x509), args);
476}476}
477477
478478
479479
=== modified file 'src/crypto/x509.h'
--- src/crypto/x509.h 2008-02-19 01:50:23 +0000
+++ src/crypto/x509.h 2009-11-09 06:00:31 +0000
@@ -16,8 +16,6 @@
16#include <Python.h>16#include <Python.h>
17#include <openssl/ssl.h>17#include <openssl/ssl.h>
1818
19extern int init_crypto_x509 (PyObject *);
20
21extern PyTypeObject crypto_X509_Type;19extern PyTypeObject crypto_X509_Type;
2220
23#define crypto_X509_Check(v) ((v)->ob_type == &crypto_X509_Type)21#define crypto_X509_Check(v) ((v)->ob_type == &crypto_X509_Type)
@@ -28,5 +26,9 @@
28 int dealloc;26 int dealloc;
29} crypto_X509Obj;27} crypto_X509Obj;
3028
29PyObject* _set_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args);
30PyObject* _get_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args);
31extern int init_crypto_x509 (PyObject *);
32
3133
32#endif34#endif
3335
=== modified file 'test/test_crypto.py'
--- test/test_crypto.py 2009-09-01 14:35:50 +0000
+++ test/test_crypto.py 2009-11-09 06:00:31 +0000
@@ -19,7 +19,12 @@
19from OpenSSL.crypto import dump_certificate, load_certificate_request19from OpenSSL.crypto import dump_certificate, load_certificate_request
20from OpenSSL.crypto import dump_certificate_request, dump_privatekey20from OpenSSL.crypto import dump_certificate_request, dump_privatekey
21from OpenSSL.crypto import PKCS7Type, load_pkcs7_data21from OpenSSL.crypto import PKCS7Type, load_pkcs7_data
22<<<<<<< TREE
22from OpenSSL.crypto import PKCS12Type, load_pkcs12, PKCS1223from OpenSSL.crypto import PKCS12Type, load_pkcs12, PKCS12
24=======
25from OpenSSL.crypto import PKCS12Type, load_pkcs12
26from OpenSSL.crypto import CRL, Revoked, load_crl
27>>>>>>> MERGE-SOURCE
23from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType28from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType
24from OpenSSL.test.util import TestCase29from OpenSSL.test.util import TestCase
2530
@@ -1532,6 +1537,214 @@
1532 self.assertTrue(isinstance(nspki, NetscapeSPKIType))1537 self.assertTrue(isinstance(nspki, NetscapeSPKIType))
15331538
15341539
1540def _runopenssl(pem, *args):
1541 """
1542 Run the command line openssl tool with the given arguments and write
1543 the given PEM to its stdin.
1544 """
1545 write, read = popen2(" ".join(("openssl",) + args), "b")
1546 write.write(pem)
1547 write.close()
1548 return read.read()
1549
1550
1551class RevokedTests(TestCase):
1552 """
1553 Tests for L{OpenSSL.crypto.Revoked}
1554 """
1555 def test_construction(self):
1556 """
1557 Confirm we can create L{OpenSSL.crypto.Revoked}. Check
1558 that it is empty.
1559 """
1560 revoked = Revoked()
1561 self.assertTrue( isinstance(revoked, Revoked) )
1562 self.assertEqual( type(revoked), Revoked )
1563 self.assertEqual( revoked.get_serial(), '00' )
1564 self.assertEqual( revoked.get_rev_date(), None )
1565 self.assertEqual( revoked.get_reason(), None )
1566
1567
1568 def test_serial(self):
1569 """
1570 Confirm we can set and get serial numbers from
1571 L{OpenSSL.crypto.Revoked}. Confirm errors are handled
1572 with grace.
1573 """
1574 revoked = Revoked()
1575 ret = revoked.set_serial('10b')
1576 self.assertEqual( ret, None )
1577 ser = revoked.get_serial()
1578 self.assertEqual( ser, '010B' )
1579
1580 revoked.set_serial('31ppp') # a type error would be nice
1581 ser = revoked.get_serial()
1582 self.assertEqual( ser, '31' )
1583
1584 self.assertRaises(ValueError, revoked.set_serial, 'pqrst')
1585 self.assertRaises(TypeError, revoked.set_serial, 100)
1586
1587
1588 def test_date(self):
1589 """
1590 Confirm we can set and get revocation dates from
1591 L{OpenSSL.crypto.Revoked}. Confirm errors are handled
1592 with grace.
1593 """
1594 revoked = Revoked()
1595 date = revoked.get_rev_date()
1596 self.assertEqual( date, None )
1597
1598 now = datetime.now().strftime("%Y%m%d%H%M%SZ")
1599 ret = revoked.set_rev_date(now)
1600 self.assertEqual( ret, None )
1601 date = revoked.get_rev_date()
1602 self.assertEqual( date, now )
1603
1604
1605 def test_reason(self):
1606 """
1607 Confirm we can set and get revocation reasons from
1608 L{OpenSSL.crypto.Revoked}. The "get" need to work
1609 as "set". Likewise, each reason of all_reasons() must work.
1610 """
1611 revoked = Revoked()
1612 for r in revoked.all_reasons():
1613 for x in xrange(2):
1614 ret = revoked.set_reason(r)
1615 self.assertEqual( ret, None )
1616 reason = revoked.get_reason()
1617 self.assertEqual( reason.lower().replace(' ',''),
1618 r.lower().replace(' ','') )
1619 r = reason # again with the resp of get
1620
1621 revoked.set_reason(None)
1622 self.assertEqual(revoked.get_reason(), None)
1623
1624
1625 def test_bad_reasons(self):
1626 """
1627 Use L{OpenSSL.crypto.Revoked.set_reason} in bad ways.
1628 """
1629 revoked = Revoked()
1630 self.assertRaises(TypeError, revoked.set_reason, 100)
1631 self.assertRaises(ValueError, revoked.set_reason, 'blue')
1632
1633
1634class CRLTests(TestCase):
1635 """
1636 Tests for L{OpenSSL.crypto.CRL}
1637 """
1638 cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
1639 pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
1640
1641 def test_construction(self):
1642 """
1643 Confirm we can create L{OpenSSL.crypto.CRL}. Check
1644 that it is empty
1645 """
1646 crl = CRL()
1647 self.assertTrue( isinstance(crl, CRL) )
1648 self.assertEqual(crl.get_revoked(), None)
1649
1650
1651 def test_export(self):
1652 """
1653 Use python to create a simple CRL with a revocation, and export
1654 the CRL in formats of PEM, DER and text. Those outputs are verified
1655 with the openssl program.
1656 """
1657 crl = CRL()
1658 revoked = Revoked()
1659 now = datetime.now().strftime("%Y%m%d%H%M%SZ")
1660 revoked.set_rev_date(now)
1661 revoked.set_serial('3ab')
1662 revoked.set_reason('sUpErSeDEd')
1663 crl.add_revoked(revoked)
1664
1665 # PEM format
1666 dumped_crl = crl.export(self.cert, self.pkey, days=20)
1667 text = _runopenssl(dumped_crl, "crl", "-noout", "-text")
1668 text.index('Serial Number: 03AB')
1669 text.index('Superseded')
1670 text.index('Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=Testing Root CA')
1671
1672 # DER format
1673 dumped_crl = crl.export(self.cert, self.pkey, FILETYPE_ASN1)
1674 text = _runopenssl(dumped_crl, "crl", "-noout", "-text", "-inform", "DER")
1675 text.index('Serial Number: 03AB')
1676 text.index('Superseded')
1677 text.index('Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=Testing Root CA')
1678
1679 # text format
1680 dumped_text = crl.export(self.cert, self.pkey, type=FILETYPE_TEXT)
1681 self.assertEqual(text, dumped_text)
1682
1683
1684
1685 def test_get_revoked(self):
1686 """
1687 Use python to create a simple CRL with two revocations.
1688 Get back the L{Revoked} using L{OpenSSL.CRL.get_revoked} and
1689 verify them.
1690 """
1691 crl = CRL()
1692
1693 revoked = Revoked()
1694 now = datetime.now().strftime("%Y%m%d%H%M%SZ")
1695 revoked.set_rev_date(now)
1696 revoked.set_serial('3ab')
1697 crl.add_revoked(revoked)
1698 revoked.set_serial('100')
1699 revoked.set_reason('sUpErSeDEd')
1700 crl.add_revoked(revoked)
1701
1702 revs = crl.get_revoked()
1703 self.assertEqual(len(revs), 2)
1704 self.assertEqual(type(revs[0]), Revoked)
1705 self.assertEqual(type(revs[1]), Revoked)
1706 self.assertEqual(revs[0].get_serial(), '03AB')
1707 self.assertEqual(revs[1].get_serial(), '0100')
1708 self.assertEqual(revs[0].get_rev_date(), now)
1709 self.assertEqual(revs[1].get_rev_date(), now)
1710
1711
1712 def test_load_crl(self):
1713 """
1714 Load a known CRL and inspect its revocations. Both
1715 PEM and DER formats are loaded.
1716 """
1717
1718 crl_txt = """
1719-----BEGIN X509 CRL-----
1720MIIBWzCBxTANBgkqhkiG9w0BAQQFADBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMC
1721SUwxEDAOBgNVBAcTB0NoaWNhZ28xEDAOBgNVBAoTB1Rlc3RpbmcxGDAWBgNVBAMT
1722D1Rlc3RpbmcgUm9vdCBDQRcNMDkwNzI2MDQzNDU2WhcNMTIwOTI3MDI0MTUyWjA8
1723MBUCAgOrGA8yMDA5MDcyNTIzMzQ1NlowIwICAQAYDzIwMDkwNzI1MjMzNDU2WjAM
1724MAoGA1UdFQQDCgEEMA0GCSqGSIb3DQEBBAUAA4GBAEBt7xTs2htdD3d4ErrcGAw1
17254dKcVnIWTutoI7xxen26Wwvh8VCsT7i/UeP+rBl9rC/kfjWjzQk3/zleaarGTpBT
17260yp4HXRFFoRhhSE/hP+eteaPXRgrsNRLHe9ZDd69wmh7J1wMDb0m81RG7kqcbsid
1727vrzEeLDRiiPl92dyyWmu
1728-----END X509 CRL-----
1729"""
1730 crl = load_crl(FILETYPE_PEM, crl_txt)
1731 revs = crl.get_revoked()
1732 self.assertEqual(len(revs), 2)
1733 self.assertEqual(revs[0].get_serial(), '03AB')
1734 self.assertEqual(revs[0].get_reason(), None)
1735 self.assertEqual(revs[1].get_serial(), '0100')
1736 self.assertEqual(revs[1].get_reason(), 'Superseded')
1737
1738 der = _runopenssl(crl_txt, "crl", "-outform", "DER")
1739 crl = load_crl(FILETYPE_ASN1, der)
1740 revs = crl.get_revoked()
1741 self.assertEqual(len(revs), 2)
1742 self.assertEqual(revs[0].get_serial(), '03AB')
1743 self.assertEqual(revs[0].get_reason(), None)
1744 self.assertEqual(revs[1].get_serial(), '0100')
1745 self.assertEqual(revs[1].get_reason(), 'Superseded')
1746
1747
15351748
1536if __name__ == '__main__':1749if __name__ == '__main__':
1537 main()1750 main()

Subscribers

People subscribed via source and target branches

to status/vote changes: