Merge lp:~rick-fdd/pyopenssl/crl_and_revoked into lp:~exarkun/pyopenssl/trunk
- crl_and_revoked
- Merge into trunk
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 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jean-Paul Calderone | Pending | ||
Review via email: mp+14640@code.launchpad.net |
Commit message
Description of the change
sebvieira (sebvieira) wrote : | # |
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.
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:/
> You are the owner of lp:~rick-fdd/pyopenssl/crl_and_revoked.
Preview Diff
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() |
Tested and works perfectly. Test units and documentation are present. Functionality would add a lot to pyOpenSSL implementation.