Hello community, here is the log from the commit of package python-openssl for openSUSE:Factory checked in at Wed Jun 10 02:20:44 CEST 2009. -------- --- python-openssl/python-openssl.changes 2008-10-13 14:15:02.000000000 +0200 +++ /mounts/work_src_done/STABLE/python-openssl/python-openssl.changes 2009-02-02 15:31:02.000000000 +0100 @@ -1,0 +2,8 @@ +Mon Feb 2 14:10:43 CET 2009 - lnussel@suse.de + +- update to 0.8 + * better thread safety +- backport patch to allow use of the default certificate store from + bzr (https://bugs.launchpad.net/pyopenssl/+bug/264956) + +------------------------------------------------------------------- calling whatdependson for head-i586 Old: ---- pyOpenSSL-0.7.tar.bz2 New: ---- pyOpenSSL-0.8-default-certstore.diff pyOpenSSL-0.8.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-openssl.spec ++++++ --- /var/tmp/diff_new_pack.p10634/_old 2009-06-10 02:20:25.000000000 +0200 +++ /var/tmp/diff_new_pack.p10634/_new 2009-06-10 02:20:25.000000000 +0200 @@ -1,7 +1,7 @@ # -# spec file for package python-openssl (Version 0.7.0) +# spec file for package python-openssl (Version 0.8.0) # -# Copyright (c) 2008 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2009 SUSE LINUX Products GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -21,15 +21,16 @@ Name: python-openssl BuildRequires: openssl-devel python-devel Summary: Python wrapper module around the OpenSSL library -Version: 0.7.0 +Version: 0.8.0 Release: 1 #Source: pyOpenSSL-%{version}.tar.bz2 -Source: pyOpenSSL-0.7.tar.bz2 +Source: pyOpenSSL-0.8.tar.bz2 Patch0: pyOpenSSL-0.6-ssize.patch Patch1: pyOpenSSL-0.7a2-noreturn.patch +Patch2: pyOpenSSL-0.8-default-certstore.diff License: LGPL v2.1 or later Group: Development/Languages/Python -Url: http://pyopenssl.sourceforge.net/ +Url: https://launchpad.net/pyopenssl/ Requires: python BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -51,17 +52,17 @@ %prep #%setup -n pyOpenSSL-%{version} -%setup -n pyOpenSSL-0.7 -%patch0 +%setup -n pyOpenSSL-0.8 +# needed? +#patch0 %patch1 +%patch2 -p0 %build #export CFLAGS="$RPM_OPT_FLAGS" python setup.py build %install -[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != / ] && rm -rf "$RPM_BUILD_ROOT" -mkdir -p "$RPM_BUILD_ROOT" python setup.py install --prefix=%{_prefix} --optimize=2 --record-rpm=INSTALLED_FILES \ --root="$RPM_BUILD_ROOT" mkdir -p $RPM_BUILD_ROOT/%{_defaultdocdir}/%{name} @@ -77,6 +78,11 @@ %doc %{_defaultdocdir}/%{name} %changelog +* Mon Feb 02 2009 lnussel@suse.de +- update to 0.8 + * better thread safety +- backport patch to allow use of the default certificate store from + bzr (https://bugs.launchpad.net/pyopenssl/+bug/264956) * Mon Oct 13 2008 matejcik@suse.cz - renamed version to 0.7.0 to avoid upgrade problems (bnc#432544) TODO switch back all %%{version} occurences with next version @@ -99,5 +105,5 @@ - updated to reflect python changes due to #149809 * Wed Jan 25 2006 mls@suse.de - converted neededforbuild to BuildRequires -* Thu Aug 26 2004 garloff@suse.de +* Wed Aug 25 2004 garloff@suse.de - Initial creation of package python-openssl (pyOpenSSL). ++++++ pyOpenSSL-0.8-default-certstore.diff ++++++ === modified file 'src/ssl/context.c' --- src/ssl/context.c 2008-09-21 22:47:06 +0000 +++ src/ssl/context.c 2008-12-29 02:09:53 +0000 @@ -238,18 +238,20 @@ \n\ Arguments: self - The Context object\n\ args - The Python argument tuple, should be:\n\ - cafile - Which file we can find the certificates\n\ + cafile - In which file we can find the certificates\n\ + capath - In which directory we can find the certificates\r\ Returns: None\n\ "; static PyObject * -ssl_Context_load_verify_locations(ssl_ContextObj *self, PyObject *args) -{ - char *cafile; +ssl_Context_load_verify_locations(ssl_ContextObj *self, PyObject *args) { + char *cafile = NULL; + char *capath = NULL; - if (!PyArg_ParseTuple(args, "s:load_verify_locations", &cafile)) + if (!PyArg_ParseTuple(args, "z|z:load_verify_locations", &cafile, &capath)) { return NULL; + } - if (!SSL_CTX_load_verify_locations(self->ctx, cafile, NULL)) + if (!SSL_CTX_load_verify_locations(self->ctx, cafile, capath)) { exception_from_error_queue(); return NULL; @@ -261,6 +263,33 @@ } } +static char ssl_Context_set_default_verify_paths_doc[] = "\n\ +Use the platform-specific CA certificate locations\n\ +\n\ +Arguments: self - The Context object\n\ + args - None\n\ +\n\ +Returns: None\n\ +"; +static PyObject * +ssl_Context_set_default_verify_paths(ssl_ContextObj *self, PyObject *args) { + if (!PyArg_ParseTuple(args, ":set_default_verify_paths")) { + return NULL; + } + + /* + * XXX Error handling for SSL_CTX_set_default_verify_paths is untested. + * -exarkun + */ + if (!SSL_CTX_set_default_verify_paths(self->ctx)) { + exception_from_error_queue(); + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +}; + + static char ssl_Context_set_passwd_cb_doc[] = "\n\ Set the passphrase callback\n\ \n\ @@ -952,6 +981,7 @@ static PyMethodDef ssl_Context_methods[] = { ADD_METHOD(load_verify_locations), ADD_METHOD(set_passwd_cb), + ADD_METHOD(set_default_verify_paths), ADD_METHOD(use_certificate_chain_file), ADD_METHOD(use_certificate_file), ADD_METHOD(use_certificate), ++++++ pyOpenSSL-0.7.tar.bz2 -> pyOpenSSL-0.8.tar.bz2 ++++++ diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/ChangeLog new/pyOpenSSL-0.8/ChangeLog --- old/pyOpenSSL-0.7/ChangeLog 2008-04-11 17:45:54.000000000 +0200 +++ new/pyOpenSSL-0.8/ChangeLog 2008-10-19 18:01:32.000000000 +0200 @@ -1,3 +1,47 @@ +2008-09-22 Jean-Paul Calderone <exarkun@twistedmatrix.com> + + * Release 0.8 + +2008-10-19 Jean-Paul Calderone <exarkun@twistedmatrix.com> + + * tsafe.py: Revert the deprecation of the thread-safe Connection + wrapper. The Connection class should not segfault if used from + multiple threads now, but it generally cannot be relied on to + produce correct results if used without the thread-safe wrapper. + * doc/pyOpenSSL.tex: Correct the documentation for the set_passwd_cb + callback parameter so that it accurately describes the required + signature. + +2008-09-22 Jean-Paul Calderone <exarkun@twistedmatrix.com> + + * Release 0.8a1 + +2008-09-21 Jean-Paul Calderone <exarkun@twistedmatrix.com> + + * src/ssl/ssl.h, src/ssl/ssl.c: Add a thread-local storage key + which will be used to store and retrieve PyThreadState pointers + whenever it is necessary to release or re-acquire the GIL. + + * src/ssl/context.c: Change global_verify_callback so that it + unconditionally manipulates the Python threadstate, rather than + checking the tstate field which is now always NULL. + +2008-04-26 Jean-Paul Calderone <exarkun@twistedmatrix.com> + + * src/ssl/context.c: Change global_passphrase_callback and + global_info_callback so that they acquire the GIL before + invoking any CPython APIs and do not release it until after they + are finished invoking all of them (based heavily on on patch + from Dan Williams). + * src/ssl/crypto.c: Initialize OpenSSL thread support so that it + is valid to use OpenSSL APIs from more than one thread (based on + patch from Dan Williams). + * test/test_crypto.py: Add tests for load_privatekey and + dump_privatekey when a passphrase or a passphrase callback is + supplied. + * test/test_ssl.py: Add tests for Context.set_passwd_cb and + Context.set_info_callback. + 2008-04-11 Jean-Paul Calderone <exarkun@twistedmatrix.com> * Release 0.7 @@ -102,7 +146,7 @@ 2004-07-19 Martin Sjögren <msjogren@gmail.com> * doc/pyOpenSSL.tex: Fix the errors regarding X509Name's field names. - + 2004-07-18 Martin Sjögren <msjogren@gmail.com> * examples/certgen.py: Fixed wrong attributes in doc string, thanks diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/doc/pyOpenSSL.tex new/pyOpenSSL-0.8/doc/pyOpenSSL.tex --- old/pyOpenSSL-0.7/doc/pyOpenSSL.tex 2008-04-11 17:45:54.000000000 +0200 +++ new/pyOpenSSL-0.8/doc/pyOpenSSL.tex 2008-10-19 18:01:32.000000000 +0200 @@ -2,7 +2,7 @@ \title{Python OpenSSL Manual} -\release{0.7} +\release{0.8} \author{Martin Sj�gren} \authoraddress{\email{martin@strakt.com}} @@ -26,22 +26,18 @@ \section{Introduction \label{intro}} -The reason this module exists at all is that the SSL support in the socket -module in the Python 2.1 distribution (which is what we used, of course I -cannot speak for later versions) is severely limited. - -When asking about SSL on the comp.lang.python newsgroup (or on -python-list@python.org) people usually pointed you to the M2Crypto package. -The M2Crypto.SSL module does implement a lot of OpenSSL's functionality but -unfortunately its error handling system does not seem to be finished, -especially for non-blocking I/O. I think that much of the reason for this -is that M2Crypto\footnote{See \url{http://www.post1.com/home/ngps/m2/}} is -developed using SWIG\footnote{See \url{http://swig.sourceforge.net/}}. This -makes it awkward to create functions that e.g. can return both an integer and -NULL since (as far as I know) you basically write C functions and SWIG makes -wrapper functions that parses the Python argument list and calls your C -function, and finally transforms your return value to a Python object. - +The reason pyOpenSSL was created is that the SSL support in the socket module +in Python 2.1 (the contemporary version of Python when the pyOpenSSL project +was begun) was severely limited. Other OpenSSL wrappers for Python at the time +were also limited, though in different ways. Unfortunately, Python's standard +library SSL support has remained weak, although other packages (such as +M2Crypto\footnote{See \url{http://chandlerproject.org/Projects/MeTooCrypto}}) +have made great advances and now equal or exceed pyOpenSSL's functionality. + +The reason pyOpenSSL continues to be maintained is that there is a significant +user community around it, as well as a large amount of software which depends +on it. It is a great benefit to many people for pyOpenSSL to continue to exist +and advance. \section{Building and Installing \label{building}} @@ -794,14 +790,14 @@ \begin{methoddesc}[Context]{set_passwd_cb}{callback\optional{, userdata}} Set the passphrase callback to \var{callback}. This function will be called -when a private key with a passphrase is loaded. -\var{callback} should take a boolean argument \var{repeat} and an arbitrary -argument \var{data} and return the passphrase entered by the user. If -\var{repeat} is true then \var{callback} should ask for the passphrase twice -and make sure that the two entries are equal. The \var{data} argument is the -\var{userdata} variable passed to the \method{set_passwd_cb} method. If an -error occurs, \var{callback} should return a false value (e.g. an empty -string). +when a private key with a passphrase is loaded. \var{callback} must accept +three positional arguments. First, an integer giving the maximum length of +the passphrase it may return. If the returned passphrase is longer than +this, it will be truncated. Second, a boolean value which will be true if +the user should be prompted for the passphrase twice and the callback should +verify that the two values supplied are equal. Third, the value given as the +\var{userdata} parameter to \method{set_passwd_cb}. If an error occurs, +\var{callback} should return a false value (e.g. an empty string). \end{methoddesc} \begin{methoddesc}[Context]{set_session_id}{name} @@ -1054,10 +1050,9 @@ is needed. Another problem is thread support. A lot of the OpenSSL I/O functions can block if the socket is in blocking mode, and then you want other Python threads to be able to do other things. The real trouble is if you've -released the thread lock to do a potentially blocking operation, and the -operation calls a callback. Then we must take the thread lock back\footnote{I'm -not sure why this is necessary, but otherwise I get a segmentation violation on -\cfunction{PyEval_CallObject}}. +released the global CPython interpreter lock to do a potentially blocking +operation, and the operation calls a callback. Then we must take the GIL back, +since calling Python APIs without holding it is not allowed. There are two solutions to the first problem, both of which are necessary. The first solution to use is if the C callback allows ''userdata'' to be passed to @@ -1070,15 +1065,17 @@ wrapper \class{Connection} object as app_data for the SSL object, and we can easily find the Python callback. -The other problem is also partially solved by app_data. Since we're associating -our wrapper objects with the ''real'' objects, we can easily access data from -the \class{Connection} object. The solution then is to simply include a -\ctype{PyThreadState} variable in the \class{Connection} declaration, and write -macros similar to \cfunction{Py_BEGIN_ALLOW_THREADS} and -\cfunction{Py_END_ALLOW_THREADS} that allows specifying of the -\ctype{PyThreadState} variable to use. Now we can simply ''begin allow -threads'' before a potentially blocking operation, and ''end allow threads'' -before calling a callback. +The other problem is solved using thread local variables. Whenever the GIL is +released before calling into an OpenSSL API, the PyThreadState pointer returned +by \cfunction{PyEval_SaveState} is stored in a global thread local variable +(using Python's own TLS API, \cfunction{PyThread_set_key_value}). When it is +necessary to re-acquire the GIL, either after the OpenSSL API returns or in a C +callback invoked by that OpenSSL API, the value of the thread local variable is +retrieved (\cfunction{PyThread_get_key_value}) and used to re-acquire the GIL. +This allows Python threads to execute while OpenSSL APIs are running and allows +use of any particular pyOpenSSL object from any Python thread, since there is +no per-thread state associated with any of these objects and since OpenSSL is +threadsafe (as long as properly initialized, as pyOpenSSL initializes it). \subsection{Acessing Socket Methods \label{socket-methods}} diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/PKG-INFO new/pyOpenSSL-0.8/PKG-INFO --- old/pyOpenSSL-0.7/PKG-INFO 2008-04-11 17:53:26.000000000 +0200 +++ new/pyOpenSSL-0.8/PKG-INFO 2008-10-19 18:01:33.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: pyOpenSSL -Version: 0.7 +Version: 0.8 Summary: Python wrapper module around the OpenSSL library Home-page: http://pyopenssl.sourceforge.net/ Author: Jean-Paul Calderone diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/setup.py new/pyOpenSSL-0.8/setup.py --- old/pyOpenSSL-0.7/setup.py 2008-04-11 17:53:24.000000000 +0200 +++ new/pyOpenSSL-0.8/setup.py 2008-09-22 02:36:54.000000000 +0200 @@ -1,3 +1,4 @@ +#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) AB Strakt 2001, All rights reserved diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/crypto/crypto.c new/pyOpenSSL-0.8/src/crypto/crypto.c --- old/pyOpenSSL-0.7/src/crypto/crypto.c 2008-03-21 23:34:42.000000000 +0100 +++ new/pyOpenSSL-0.8/src/crypto/crypto.c 2008-09-22 02:36:55.000000000 +0200 @@ -592,7 +592,7 @@ if (py_pkey) { py_pkey->initialized = 0; } - return py_pkey; + return (PyObject *)py_pkey; } static char crypto_X509Extension_doc[] = "\n\ @@ -694,6 +694,73 @@ { NULL, NULL } }; + +#ifdef WITH_THREAD + +#include <pythread.h> + +/** + * This array will store all of the mutexes available to OpenSSL. + */ +static PyThread_type_lock *mutex_buf = NULL; + + +/** + * Callback function supplied to OpenSSL to acquire or release a lock. + * + */ +static void locking_function(int mode, int n, const char * file, int line) { + if (mode & CRYPTO_LOCK) { + PyThread_acquire_lock(mutex_buf[n], WAIT_LOCK); + } else { + PyThread_release_lock(mutex_buf[n]); + } +} + + +/** + * Initialize OpenSSL for use from multiple threads. + * + * Returns: 0 if initialization fails, 1 otherwise. + */ +static int init_openssl_threads(void) { + int i; + + mutex_buf = (PyThread_type_lock *)malloc( + CRYPTO_num_locks() * sizeof(PyThread_type_lock)); + if (!mutex_buf) { + return 0; + } + for (i = 0; i < CRYPTO_num_locks(); ++i) { + mutex_buf[i] = PyThread_allocate_lock(); + } + CRYPTO_set_id_callback(PyThread_get_thread_ident); + CRYPTO_set_locking_callback(locking_function); + return 1; +} + +/* /** */ +/* * Clean up after OpenSSL thread initialization. */ +/* */ */ +/* static int deinit_openssl_threads() { */ +/* int i; */ + +/* if (!mutex_buf) { */ +/* return 0; */ +/* } */ +/* CRYPTO_set_id_callback(NULL); */ +/* CRYPTO_set_locking_callback(NULL); */ +/* for (i = 0; i < CRYPTO_num_locks(); i++) { */ +/* PyThread_free_lock(mutex_buf[i]); */ +/* } */ +/* free(mutex_buf); */ +/* mutex_buf = NULL; */ +/* return 1; */ +/* } */ + +#endif + + /* * Initialize crypto sub module * @@ -739,6 +806,10 @@ PyModule_AddIntConstant(module, "TYPE_DSA", crypto_TYPE_DSA); dict = PyModule_GetDict(module); +#ifdef WITH_THREAD + if (!init_openssl_threads()) + goto error; +#endif if (!init_crypto_x509(dict)) goto error; if (!init_crypto_x509name(dict)) diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/crypto/netscape_spki.c new/pyOpenSSL-0.8/src/crypto/netscape_spki.c --- old/pyOpenSSL-0.7/src/crypto/netscape_spki.c 2008-03-03 21:16:06.000000000 +0100 +++ new/pyOpenSSL-0.8/src/crypto/netscape_spki.c 2008-09-22 02:36:55.000000000 +0200 @@ -9,9 +9,6 @@ #define crypto_MODULE #include "crypto.h" -static char *CVSid = "@(#) $Id: netscape_spki.c,v 1.1 2004/08/09 13:41:25 martin Exp $"; - - /* * Constructor for Nestcape_SPKI, never called by Python code directly * diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/crypto/pkcs12.c new/pyOpenSSL-0.8/src/crypto/pkcs12.c --- old/pyOpenSSL-0.7/src/crypto/pkcs12.c 2008-03-03 21:16:06.000000000 +0100 +++ new/pyOpenSSL-0.8/src/crypto/pkcs12.c 2008-09-22 02:36:55.000000000 +0200 @@ -14,8 +14,6 @@ #define crypto_MODULE #include "crypto.h" -static char *CVSid = "@(#) $Id: pkcs12.c,v 1.3 2003/01/09 17:08:32 martin Exp $"; - /* * PKCS12 is a standard exchange format for digital certificates. * See e.g. the OpenSSL homepage http://www.openssl.org/ for more information diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/crypto/pkcs7.c new/pyOpenSSL-0.8/src/crypto/pkcs7.c --- old/pyOpenSSL-0.7/src/crypto/pkcs7.c 2008-03-03 21:16:06.000000000 +0100 +++ new/pyOpenSSL-0.8/src/crypto/pkcs7.c 2008-09-22 02:36:55.000000000 +0200 @@ -11,8 +11,6 @@ #define crypto_MODULE #include "crypto.h" -static char *CVSid = "@(#) $Id: pkcs7.c,v 1.2 2002/07/09 12:55:13 martin Exp $"; - static char crypto_PKCS7_type_is_signed_doc[] = "\n\ Check if this NID_pkcs7_signed object\n\ \n\ diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/crypto/x509.c new/pyOpenSSL-0.8/src/crypto/x509.c --- old/pyOpenSSL-0.7/src/crypto/x509.c 2008-03-25 20:23:37.000000000 +0100 +++ new/pyOpenSSL-0.8/src/crypto/x509.c 2008-09-22 02:36:55.000000000 +0200 @@ -323,7 +323,7 @@ if (py_pkey != NULL) { py_pkey->only_public = 1; } - return py_pkey; + return (PyObject *)py_pkey; } static char crypto_X509_set_pubkey_doc[] = "\n\ diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/crypto/x509name.c new/pyOpenSSL-0.8/src/crypto/x509name.c --- old/pyOpenSSL-0.7/src/crypto/x509name.c 2008-04-11 17:45:54.000000000 +0200 +++ new/pyOpenSSL-0.8/src/crypto/x509name.c 2008-09-22 02:36:55.000000000 +0200 @@ -301,7 +301,6 @@ ASN1_STRING *fval; int nid; int l; - unsigned char buf[100]; unsigned char *str; PyObject *tuple; diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/crypto/x509req.c new/pyOpenSSL-0.8/src/crypto/x509req.c --- old/pyOpenSSL-0.7/src/crypto/x509req.c 2008-03-21 23:34:42.000000000 +0100 +++ new/pyOpenSSL-0.8/src/crypto/x509req.c 2008-09-22 02:36:55.000000000 +0200 @@ -70,7 +70,7 @@ if (py_pkey != NULL) { py_pkey->only_public = 1; } - return py_pkey; + return (PyObject *)py_pkey; } static char crypto_X509Req_set_pubkey_doc[] = "\n\ diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/crypto/x509store.c new/pyOpenSSL-0.8/src/crypto/x509store.c --- old/pyOpenSSL-0.7/src/crypto/x509store.c 2008-03-03 21:16:06.000000000 +0100 +++ new/pyOpenSSL-0.8/src/crypto/x509store.c 2008-09-22 02:36:55.000000000 +0200 @@ -10,8 +10,6 @@ #define crypto_MODULE #include "crypto.h" -static char *CVSid = "@(#) $Id: x509store.c,v 1.9 2002/09/04 22:24:59 iko Exp $"; - static char crypto_X509Store_add_cert_doc[] = "\n\ Add a certificate\n\ \n\ diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/rand/rand.c new/pyOpenSSL-0.8/src/rand/rand.c --- old/pyOpenSSL-0.7/src/rand/rand.c 2008-03-03 21:16:06.000000000 +0100 +++ new/pyOpenSSL-0.8/src/rand/rand.c 2008-09-22 02:36:55.000000000 +0200 @@ -25,8 +25,6 @@ See the file RATIONALE for a short explanation of why this module was written.\n\ "; -static char *CVSid = "@(#) $Id: rand.c,v 1.10 2002/07/08 11:06:01 martin Exp $"; - static char rand_add_doc[] = "\n\ Add data with a given entropy to the PRNG\n\ \n\ diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/ssl/connection.h new/pyOpenSSL-0.8/src/ssl/connection.h --- old/pyOpenSSL-0.7/src/ssl/connection.h 2008-03-03 21:16:06.000000000 +0100 +++ new/pyOpenSSL-0.8/src/ssl/connection.h 2008-09-22 14:50:53.000000000 +0200 @@ -42,7 +42,7 @@ SSL *ssl; ssl_ContextObj *context; PyObject *socket; - PyThreadState *tstate; + PyThreadState *tstate; /* This field is no longer used. */ PyObject *app_data; } ssl_ConnectionObj; diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/ssl/context.c new/pyOpenSSL-0.8/src/ssl/context.c --- old/pyOpenSSL-0.7/src/ssl/context.c 2008-03-21 23:34:42.000000000 +0100 +++ new/pyOpenSSL-0.8/src/ssl/context.c 2008-09-22 14:50:53.000000000 +0200 @@ -51,7 +51,9 @@ */ /* - * Globally defined passphrase callback. + * Globally defined passphrase callback. This is called from OpenSSL + * internally. The GIL will not be held when this function is invoked. It + * must not be held when the function returns. * * Arguments: buf - Buffer to store the returned passphrase in * maxlen - Maximum length of the passphrase @@ -64,49 +66,77 @@ static int global_passphrase_callback(char *buf, int maxlen, int verify, void *arg) { - int len; + /* + * Initialize len here because we're always going to return it, and we + * might jump to the return before it gets initialized in any other way. + */ + int len = 0; char *str; PyObject *argv, *ret = NULL; ssl_ContextObj *ctx = (ssl_ContextObj *)arg; + /* + * GIL isn't held yet. First things first - acquire it, or any Python API + * we invoke might segfault or blow up the sun. The reverse will be done + * before returning. + */ + MY_END_ALLOW_THREADS(ctx->tstate); + /* The Python callback is called with a (maxlen,verify,userdata) tuple */ argv = Py_BuildValue("(iiO)", maxlen, verify, ctx->passphrase_userdata); - if (ctx->tstate != NULL) - { - /* We need to get back our thread state before calling the callback */ - MY_END_ALLOW_THREADS(ctx->tstate); - ret = PyEval_CallObject(ctx->passphrase_callback, argv); - MY_BEGIN_ALLOW_THREADS(ctx->tstate); - } - else - { - ret = PyEval_CallObject(ctx->passphrase_callback, argv); - } + + /* + * XXX Didn't check argv to see if it was NULL. -exarkun + */ + ret = PyEval_CallObject(ctx->passphrase_callback, argv); Py_DECREF(argv); - if (ret == NULL) - return 0; + if (ret == NULL) { + /* + * XXX The callback raised an exception. At the very least, it should + * be printed out here. An *actual* solution would be to raise it up + * through OpenSSL. That might be a bit tricky, but it's probably + * possible. -exarkun + */ + goto out; + } - if (!PyObject_IsTrue(ret)) - { + if (!PyObject_IsTrue(ret)) { + /* + * Returned "", or None, or something. Treat it as no passphrase. + */ Py_DECREF(ret); - return 0; + goto out; } - if (!PyString_Check(ret)) - { + if (!PyString_Check(ret)) { + /* + * XXX Returned something that wasn't a string. This is bogus. We + * should report an error or raise an exception (again, through OpenSSL + * - tricky). -exarkun + */ Py_DECREF(ret); - return 0; + goto out; } len = PyString_Size(ret); - if (len > maxlen) + if (len > maxlen) { + /* + * XXX Returned more than we said they were allowed to return. Report + * an error or raise an exception (tricky blah blah). -exarkun + */ len = maxlen; + } str = PyString_AsString(ret); strncpy(buf, str, len); Py_XDECREF(ret); + out: + /* + * This function is returning into OpenSSL. Release the GIL again. + */ + MY_BEGIN_ALLOW_THREADS(ctx->tstate); return len; } @@ -127,19 +157,17 @@ ssl_ConnectionObj *conn; crypto_X509Obj *cert; int errnum, errdepth, c_ret, use_thread_state; - + // Get Connection object to check thread state ssl = (SSL *)X509_STORE_CTX_get_app_data(x509_ctx); conn = (ssl_ConnectionObj *)SSL_get_app_data(ssl); - - use_thread_state = conn->tstate != NULL; - if (use_thread_state) - MY_END_ALLOW_THREADS(conn->tstate); - + + MY_END_ALLOW_THREADS(conn->tstate); + cert = crypto_X509_New(X509_STORE_CTX_get_current_cert(x509_ctx), 0); errnum = X509_STORE_CTX_get_error(x509_ctx); errdepth = X509_STORE_CTX_get_error_depth(x509_ctx); - + argv = Py_BuildValue("(OOiii)", (PyObject *)conn, (PyObject *)cert, errnum, errdepth, ok); Py_DECREF(cert); @@ -154,13 +182,14 @@ c_ret = 0; } - if (use_thread_state) - MY_BEGIN_ALLOW_THREADS(conn->tstate); + MY_BEGIN_ALLOW_THREADS(conn->tstate); return c_ret; } /* - * Globally defined info callback + * Globally defined info callback. This is called from OpenSSL internally. + * The GIL will not be held when this function is invoked. It must not be held + * when the function returns. * * Arguments: ssl - The Connection * where - The part of the SSL code that called us @@ -173,28 +202,30 @@ ssl_ConnectionObj *conn = (ssl_ConnectionObj *)SSL_get_app_data(ssl); PyObject *argv, *ret; + /* + * GIL isn't held yet. First things first - acquire it, or any Python API + * we invoke might segfault or blow up the sun. The reverse will be done + * before returning. + */ + MY_END_ALLOW_THREADS(conn->tstate); + argv = Py_BuildValue("(Oii)", (PyObject *)conn, where, _ret); - if (conn->tstate != NULL) - { - /* We need to get back our thread state before calling the callback */ - MY_END_ALLOW_THREADS(conn->tstate); - ret = PyEval_CallObject(conn->context->info_callback, argv); - if (ret == NULL) - PyErr_Clear(); - else - Py_DECREF(ret); - MY_BEGIN_ALLOW_THREADS(conn->tstate); - } - else - { - ret = PyEval_CallObject(conn->context->info_callback, argv); - if (ret == NULL) - PyErr_Clear(); - else - Py_DECREF(ret); - } + ret = PyEval_CallObject(conn->context->info_callback, argv); Py_DECREF(argv); + if (ret == NULL) { + /* + * XXX - This should be reported somehow. -exarkun + */ + PyErr_Clear(); + } else { + Py_DECREF(ret); + } + + /* + * This function is returning into OpenSSL. Release the GIL again. + */ + MY_BEGIN_ALLOW_THREADS(conn->tstate); return; } diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/ssl/ssl.c new/pyOpenSSL-0.8/src/ssl/ssl.c --- old/pyOpenSSL-0.7/src/ssl/ssl.c 2008-03-21 23:34:42.000000000 +0100 +++ new/pyOpenSSL-0.8/src/ssl/ssl.c 2008-09-22 14:50:53.000000000 +0200 @@ -32,6 +32,8 @@ void **crypto_API; +int _pyOpenSSL_tstate_key; + /* Exceptions defined by the SSL submodule */ PyObject *ssl_Error, /* Base class */ *ssl_ZeroReturnError, /* Used with SSL_get_error */ @@ -201,6 +203,13 @@ if (!init_ssl_connection(dict)) goto error; -error: +#ifdef WITH_THREAD + /* + * Initialize this module's threading support structures. + */ + _pyOpenSSL_tstate_key = PyThread_create_key(); +#endif + + error: ; } diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/ssl/ssl.h new/pyOpenSSL-0.8/src/ssl/ssl.h --- old/pyOpenSSL-0.7/src/ssl/ssl.h 2008-03-03 21:16:06.000000000 +0100 +++ new/pyOpenSSL-0.8/src/ssl/ssl.h 2008-09-22 14:50:53.000000000 +0200 @@ -14,6 +14,7 @@ #define PyOpenSSL_SSL_H_ #include <Python.h> +#include <pythread.h> #include "context.h" #include "connection.h" #include "../util.h" @@ -45,6 +46,10 @@ #define ssl_API_pointers 2 +#ifdef WITH_THREAD +extern int _pyOpenSSL_tstate_key; +#endif /* WITH_THREAD */ + #ifdef SSL_MODULE extern ssl_Context_New_RETURN ssl_Context_New ssl_Context_New_PROTO; diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/util.c new/pyOpenSSL-0.8/src/util.c --- old/pyOpenSSL-0.7/src/util.c 2008-03-03 21:16:06.000000000 +0100 +++ new/pyOpenSSL-0.8/src/util.c 2008-09-22 02:36:55.000000000 +0200 @@ -11,9 +11,6 @@ #include <Python.h> #include "util.h" -static char *CVSid = "@(#) $Id: util.c,v 1.5 2001/08/09 11:26:36 martin Exp $"; - - /* * Flush OpenSSL's error queue and return a list of errors (a (library, * function, reason) string tuple) diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/src/util.h new/pyOpenSSL-0.8/src/util.h --- old/pyOpenSSL-0.7/src/util.h 2008-03-03 21:16:06.000000000 +0100 +++ new/pyOpenSSL-0.8/src/util.h 2008-09-22 14:50:53.000000000 +0200 @@ -30,10 +30,21 @@ * WHERE to save the thread state. */ #ifdef WITH_THREAD -# define MY_BEGIN_ALLOW_THREADS(st) \ - { st = PyEval_SaveThread(); } -# define MY_END_ALLOW_THREADS(st) \ - { PyEval_RestoreThread(st); st = NULL; } + +/* + * Get the current Python threadstate and put it somewhere any code running + * in this thread can get it, if it needs to restore the threadstate to run + * some Python. + */ +# define MY_BEGIN_ALLOW_THREADS(ignored) \ + PyThread_set_key_value(_pyOpenSSL_tstate_key, PyEval_SaveThread()); + +/* + * Get the previous Python threadstate and restore it. + */ +# define MY_END_ALLOW_THREADS(ignored) \ + PyEval_RestoreThread(PyThread_get_key_value(_pyOpenSSL_tstate_key)); + #else # define MY_BEGIN_ALLOW_THREADS(st) # define MY_END_ALLOW_THREADS(st) { st = NULL; } diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/test/test_crypto.py new/pyOpenSSL-0.8/test/test_crypto.py --- old/pyOpenSSL-0.7/test/test_crypto.py 2008-04-11 17:45:54.000000000 +0200 +++ new/pyOpenSSL-0.8/test/test_crypto.py 2008-09-22 02:36:55.000000000 +0200 @@ -9,7 +9,68 @@ from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType from OpenSSL.crypto import X509Req, X509ReqType -from OpenSSL.crypto import FILETYPE_PEM, load_certificate +from OpenSSL.crypto import FILETYPE_PEM, load_certificate, load_privatekey +from OpenSSL.crypto import dump_privatekey + + +cleartextPrivateKeyPEM = ( + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIICXAIBAAKBgQDaemNe1syksAbFFpF3aoOrZ18vB/IQNZrAjFqXPv9iieJm7+Tc\n" + "g+lA/v0qmoEKrpT2xfwxXmvZwBNM4ZhyRC3DPIFEyJV7/3IA1p5iuMY/GJI1VIgn\n" + "aikQCnrsyxtaRpsMBeZRniaVzcUJ+XnEdFGEjlo+k0xlwfVclDEMwgpXAQIDAQAB\n" + "AoGBALi0a7pMQqqgnriVAdpBVJveQtxSDVWi2/gZMKVZfzNheuSnv4amhtaKPKJ+\n" + "CMZtHkcazsE2IFvxRN/kgato9H3gJqq8nq2CkdpdLNVKBoxiCtkLfutdY4SQLtoY\n" + "USN7exk131pchsAJXYlR6mCW+ZP+E523cNwpPgsyKxVbmXSBAkEA9470fy2W0jFM\n" + "taZFslpntKSzbvn6JmdtjtvWrM1bBaeeqFiGBuQFYg46VaCUaeRWYw02jmYAsDYh\n" + "ZQavmXThaQJBAOHtlAQ0IJJEiMZr6vtVPH32fmbthSv1AUSYPzKqdlQrUnOXPQXu\n" + "z70cFoLG1TvPF5rBxbOkbQ/s8/ka5ZjPfdkCQCeC7YsO36+UpsWnUCBzRXITh4AC\n" + "7eYLQ/U1KUJTVF/GrQ/5cQrQgftwgecAxi9Qfmk4xqhbp2h4e0QAmS5I9WECQH02\n" + "0QwrX8nxFeTytr8pFGezj4a4KVCdb2B3CL+p3f70K7RIo9d/7b6frJI6ZL/LHQf2\n" + "UP4pKRDkgKsVDx7MELECQGm072/Z7vmb03h/uE95IYJOgY4nfmYs0QKA9Is18wUz\n" + "DpjfE33p0Ha6GO1VZRIQoqE24F8o5oimy3BEjryFuw4=\n" + "-----END RSA PRIVATE KEY-----\n") + + +cleartextCertificatePEM = ( + "-----BEGIN CERTIFICATE-----\n" + "MIICfTCCAeYCAQEwDQYJKoZIhvcNAQEEBQAwgYYxCzAJBgNVBAYTAlVTMRkwFwYD\n" + "VQQDExBweW9wZW5zc2wuc2YubmV0MREwDwYDVQQHEwhOZXcgWW9yazESMBAGA1UE\n" + "ChMJUHlPcGVuU1NMMREwDwYDVQQIEwhOZXcgWW9yazEQMA4GCSqGSIb3DQEJARYB\n" + "IDEQMA4GA1UECxMHVGVzdGluZzAeFw0wODAzMjUxOTA0MTNaFw0wOTAzMjUxOTA0\n" + "MTNaMIGGMQswCQYDVQQGEwJVUzEZMBcGA1UEAxMQcHlvcGVuc3NsLnNmLm5ldDER\n" + "MA8GA1UEBxMITmV3IFlvcmsxEjAQBgNVBAoTCVB5T3BlblNTTDERMA8GA1UECBMI\n" + "TmV3IFlvcmsxEDAOBgkqhkiG9w0BCQEWASAxEDAOBgNVBAsTB1Rlc3RpbmcwgZ8w\n" + "DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANp6Y17WzKSwBsUWkXdqg6tnXy8H8hA1\n" + "msCMWpc+/2KJ4mbv5NyD6UD+/SqagQqulPbF/DFea9nAE0zhmHJELcM8gUTIlXv/\n" + "cgDWnmK4xj8YkjVUiCdqKRAKeuzLG1pGmwwF5lGeJpXNxQn5ecR0UYSOWj6TTGXB\n" + "9VyUMQzCClcBAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAmm0Vzvv1O91WLl2LnF2P\n" + "q55LJdOnJbCCXIgxLdoVmvYAz1ZJq1eGKgKWI5QLgxiSzJLEU7KK//aVfiZzoCd5\n" + "RipBiEEMEV4eAY317bHPwPP+4Bj9t0l8AsDLseC5vLRHgxrLEu3bn08DYx6imB5Q\n" + "UBj849/xpszEM7BhwKE0GiQ=\n" + "-----END CERTIFICATE-----\n") + + +encryptedPrivateKeyPEM = ( + "-----BEGIN RSA PRIVATE KEY-----\n" + "Proc-Type: 4,ENCRYPTED\n" + "DEK-Info: BF-CBC,8306665233D056B1\n" + "\n" + "BwxghOcX1F+M108qRGBfpUBrfaeKOszDEV18OjEE55p0yGsiDxvdol3c4bwI5ITy\n" + "ltP8w9O33CDUCjr+Ymj8xLpPP60TTfr/aHq+2fEuG4TfkeHb5fVYm0mgVnaOhJs3\n" + "a2n5IL/KNCdP3zMZa0IaMJ0M+VK90SLpq5nzXOWkufLyZL1+n8srkk06gepmHS7L\n" + "rH3rALNboG8yTH1qjE8PwcMrJAQfRMd4/4RTQv+4pUuKj7I2en+YwSQ/gomy7qN1\n" + "3s/gMgV/2GUbEcTVch4thZ9l3WsX18V76rBQkiZ7yrJkxwNMv+Qc2GfHtBnsXAyA\n" + "0nIE4Mm/OQqX8h7EJ4c2s1DMGVS0YZGU+75HN0A3iD01h8C5utqSScWzBA45j/Vy\n" + "3aypQVqQeW7kBMQlpc6pHvJ1EsjiAJRCto7tZNLxRdjMKBV4w75JNLaAFSraqA+R\n" + "/WPcdcXAQuhmCeh31fzmVOHJGRF7/5pAR/b7AnFTD4YbYVcglNis/jpdiI9k2AYP\n" + "wZNwXOIh6Ibq5hMvyV4/pySyLbgDOrfrOGpi8N6lBbzewByYQKiXwUEZf+Y5499/\n" + "CckajBhgYynPpe6mgsSeklWGc845iIwAtzavBNZIkn1hKP1P+TFjbl2O75u/9JLJ\n" + "6i4IFYCyQmwiHX8nTR717SpCN2gyZ2HrX7z2mKP/KokkAX2yidwoKh9FMUV5lOGO\n" + "JPc4MfPo4lPB7SP30AtOh7y7zlS3x8Uo0+0wCg5Z5Fn/73x3W+p5nyI0G9n7RGzL\n" + "ZeCWLdG/Cm6ZyIpYZGbZ5m+U3Fr6/El9V6LSxrB1TB+8G1NTdLlbeA==\n" + "-----END RSA PRIVATE KEY-----\n") +encryptedPrivateKeyPEMPassphrase = "foobar" + class _Python23TestCaseHelper: # Python 2.3 compatibility. @@ -381,39 +442,7 @@ """ Tests for L{OpenSSL.crypto.X509}. """ - pemData = """ ------BEGIN CERTIFICATE----- -MIICfTCCAeYCAQEwDQYJKoZIhvcNAQEEBQAwgYYxCzAJBgNVBAYTAlVTMRkwFwYD -VQQDExBweW9wZW5zc2wuc2YubmV0MREwDwYDVQQHEwhOZXcgWW9yazESMBAGA1UE -ChMJUHlPcGVuU1NMMREwDwYDVQQIEwhOZXcgWW9yazEQMA4GCSqGSIb3DQEJARYB -IDEQMA4GA1UECxMHVGVzdGluZzAeFw0wODAzMjUxOTA0MTNaFw0wOTAzMjUxOTA0 -MTNaMIGGMQswCQYDVQQGEwJVUzEZMBcGA1UEAxMQcHlvcGVuc3NsLnNmLm5ldDER -MA8GA1UEBxMITmV3IFlvcmsxEjAQBgNVBAoTCVB5T3BlblNTTDERMA8GA1UECBMI -TmV3IFlvcmsxEDAOBgkqhkiG9w0BCQEWASAxEDAOBgNVBAsTB1Rlc3RpbmcwgZ8w -DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANp6Y17WzKSwBsUWkXdqg6tnXy8H8hA1 -msCMWpc+/2KJ4mbv5NyD6UD+/SqagQqulPbF/DFea9nAE0zhmHJELcM8gUTIlXv/ -cgDWnmK4xj8YkjVUiCdqKRAKeuzLG1pGmwwF5lGeJpXNxQn5ecR0UYSOWj6TTGXB -9VyUMQzCClcBAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAmm0Vzvv1O91WLl2LnF2P -q55LJdOnJbCCXIgxLdoVmvYAz1ZJq1eGKgKWI5QLgxiSzJLEU7KK//aVfiZzoCd5 -RipBiEEMEV4eAY317bHPwPP+4Bj9t0l8AsDLseC5vLRHgxrLEu3bn08DYx6imB5Q -UBj849/xpszEM7BhwKE0GiQ= ------END CERTIFICATE----- ------BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQDaemNe1syksAbFFpF3aoOrZ18vB/IQNZrAjFqXPv9iieJm7+Tc -g+lA/v0qmoEKrpT2xfwxXmvZwBNM4ZhyRC3DPIFEyJV7/3IA1p5iuMY/GJI1VIgn -aikQCnrsyxtaRpsMBeZRniaVzcUJ+XnEdFGEjlo+k0xlwfVclDEMwgpXAQIDAQAB -AoGBALi0a7pMQqqgnriVAdpBVJveQtxSDVWi2/gZMKVZfzNheuSnv4amhtaKPKJ+ -CMZtHkcazsE2IFvxRN/kgato9H3gJqq8nq2CkdpdLNVKBoxiCtkLfutdY4SQLtoY -USN7exk131pchsAJXYlR6mCW+ZP+E523cNwpPgsyKxVbmXSBAkEA9470fy2W0jFM -taZFslpntKSzbvn6JmdtjtvWrM1bBaeeqFiGBuQFYg46VaCUaeRWYw02jmYAsDYh -ZQavmXThaQJBAOHtlAQ0IJJEiMZr6vtVPH32fmbthSv1AUSYPzKqdlQrUnOXPQXu -z70cFoLG1TvPF5rBxbOkbQ/s8/ka5ZjPfdkCQCeC7YsO36+UpsWnUCBzRXITh4AC -7eYLQ/U1KUJTVF/GrQ/5cQrQgftwgecAxi9Qfmk4xqhbp2h4e0QAmS5I9WECQH02 -0QwrX8nxFeTytr8pFGezj4a4KVCdb2B3CL+p3f70K7RIo9d/7b6frJI6ZL/LHQf2 -UP4pKRDkgKsVDx7MELECQGm072/Z7vmb03h/uE95IYJOgY4nfmYs0QKA9Is18wUz -DpjfE33p0Ha6GO1VZRIQoqE24F8o5oimy3BEjryFuw4= ------END RSA PRIVATE KEY----- -""" + pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM def signable(self): """ @@ -534,3 +563,93 @@ self.assertEqual( cert.digest("md5"), "A8:EB:07:F8:53:25:0A:F2:56:05:C5:A5:C4:C4:C7:15") + + + +class FunctionTests(TestCase, _Python23TestCaseHelper): + """ + Tests for free-functions in the L{OpenSSL.crypto} module. + """ + def test_load_privatekey_wrongPassphrase(self): + """ + L{load_privatekey} raises L{OpenSSL.crypto.Error} when it is passed an + encrypted PEM and an incorrect passphrase. + """ + self.assertRaises( + Error, + load_privatekey, FILETYPE_PEM, encryptedPrivateKeyPEM, "quack") + + + def test_load_privatekey_passphrase(self): + """ + L{load_privatekey} can create a L{PKey} object from an encrypted PEM + string if given the passphrase. + """ + key = load_privatekey( + FILETYPE_PEM, encryptedPrivateKeyPEM, + encryptedPrivateKeyPEMPassphrase) + self.assertTrue(isinstance(key, PKeyType)) + + + def test_load_privatekey_wrongPassphraseCallback(self): + """ + L{load_privatekey} raises L{OpenSSL.crypto.Error} when it is passed an + encrypted PEM and a passphrase callback which returns an incorrect + passphrase. + """ + called = [] + def cb(*a): + called.append(None) + return "quack" + self.assertRaises( + Error, + load_privatekey, FILETYPE_PEM, encryptedPrivateKeyPEM, cb) + self.assertTrue(called) + + def test_load_privatekey_passphraseCallback(self): + """ + L{load_privatekey} can create a L{PKey} object from an encrypted PEM + string if given a passphrase callback which returns the correct + password. + """ + called = [] + def cb(writing): + called.append(writing) + return encryptedPrivateKeyPEMPassphrase + key = load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, cb) + self.assertTrue(isinstance(key, PKeyType)) + self.assertEqual(called, [False]) + + + def test_dump_privatekey_passphrase(self): + """ + L{dump_privatekey} writes an encrypted PEM when given a passphrase. + """ + passphrase = "foo" + key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) + pem = dump_privatekey(FILETYPE_PEM, key, "blowfish", passphrase) + self.assertTrue(isinstance(pem, str)) + loadedKey = load_privatekey(FILETYPE_PEM, pem, passphrase) + self.assertTrue(isinstance(loadedKey, PKeyType)) + self.assertEqual(loadedKey.type(), key.type()) + self.assertEqual(loadedKey.bits(), key.bits()) + + + def test_dump_privatekey_passphraseCallback(self): + """ + L{dump_privatekey} writes an encrypted PEM when given a callback which + returns the correct passphrase. + """ + passphrase = "foo" + called = [] + def cb(writing): + called.append(writing) + return passphrase + key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) + pem = dump_privatekey(FILETYPE_PEM, key, "blowfish", cb) + self.assertTrue(isinstance(pem, str)) + self.assertEqual(called, [True]) + loadedKey = load_privatekey(FILETYPE_PEM, pem, passphrase) + self.assertTrue(isinstance(loadedKey, PKeyType)) + self.assertEqual(loadedKey.type(), key.type()) + self.assertEqual(loadedKey.bits(), key.bits()) diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/test/test_ssl.py new/pyOpenSSL-0.8/test/test_ssl.py --- old/pyOpenSSL-0.7/test/test_ssl.py 2008-03-21 23:34:42.000000000 +0100 +++ new/pyOpenSSL-0.8/test/test_ssl.py 2008-09-22 02:36:55.000000000 +0200 @@ -5,16 +5,27 @@ """ from unittest import TestCase +from tempfile import mktemp +from socket import socket -from OpenSSL.crypto import TYPE_RSA, PKey -from OpenSSL.SSL import Context +from OpenSSL.crypto import TYPE_RSA, FILETYPE_PEM, PKey, dump_privatekey, load_certificate, load_privatekey +from OpenSSL.SSL import WantReadError, Context, Connection from OpenSSL.SSL import SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, TLSv1_METHOD +from OpenSSL.test.test_crypto import _Python23TestCaseHelper, cleartextCertificatePEM, cleartextPrivateKeyPEM -class ContextTests(TestCase): + +class ContextTests(TestCase, _Python23TestCaseHelper): """ Unit tests for L{OpenSSL.SSL.Context}. """ + def mktemp(self): + """ + Pathetic substitute for twisted.trial.unittest.TestCase.mktemp. + """ + return mktemp(dir=".") + + def test_method(self): """ L{Context} can be instantiated with one of L{SSLv2_METHOD}, @@ -35,3 +46,72 @@ ctx = Context(TLSv1_METHOD) ctx.use_privatekey(key) self.assertRaises(TypeError, ctx.use_privatekey, "") + + + def test_set_passwd_cb(self): + """ + L{Context.set_passwd_cb} accepts a callable which will be invoked when + a private key is loaded from an encrypted PEM. + """ + key = PKey() + key.generate_key(TYPE_RSA, 128) + pemFile = self.mktemp() + fObj = file(pemFile, 'w') + passphrase = "foobar" + fObj.write(dump_privatekey(FILETYPE_PEM, key, "blowfish", passphrase)) + fObj.close() + + calledWith = [] + def passphraseCallback(maxlen, verify, extra): + calledWith.append((maxlen, verify, extra)) + return passphrase + context = Context(TLSv1_METHOD) + context.set_passwd_cb(passphraseCallback) + context.use_privatekey_file(pemFile) + self.assertTrue(len(calledWith), 1) + self.assertTrue(isinstance(calledWith[0][0], int)) + self.assertTrue(isinstance(calledWith[0][1], int)) + self.assertEqual(calledWith[0][2], None) + + + def test_set_info_callback(self): + """ + L{Context.set_info_callback} accepts a callable which will be invoked + when certain information about an SSL connection is available. + """ + port = socket() + port.bind(('', 0)) + port.listen(1) + + client = socket() + client.setblocking(False) + client.connect_ex(port.getsockname()) + + clientSSL = Connection(Context(TLSv1_METHOD), client) + clientSSL.set_connect_state() + + called = [] + def info(conn, where, ret): + called.append((conn, where, ret)) + context = Context(TLSv1_METHOD) + context.set_info_callback(info) + context.use_certificate( + load_certificate(FILETYPE_PEM, cleartextCertificatePEM)) + context.use_privatekey( + load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)) + + server, ignored = port.accept() + server.setblocking(False) + + serverSSL = Connection(context, server) + serverSSL.set_accept_state() + + while not called: + for ssl in clientSSL, serverSSL: + try: + ssl.do_handshake() + except WantReadError: + pass + + # Kind of lame. Just make sure it got called somehow. + self.assertTrue(called) diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/TODO new/pyOpenSSL-0.8/TODO --- old/pyOpenSSL-0.7/TODO 2008-03-03 21:16:05.000000000 +0100 +++ new/pyOpenSSL-0.8/TODO 2008-09-22 14:50:53.000000000 +0200 @@ -3,7 +3,6 @@ * Think more carefully about the relation between X509 and X509_NAME _set_{subject,issuer} dup the new name and free the old one. * Consider Pyrex -* Update tsafe.Connection and make sure the docs are correct * Updated docs! (rpm, ...) * _Somehow_ get makefile to work! * httpslib, imapslib, ftpslib? diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/pyOpenSSL-0.7/version.py new/pyOpenSSL-0.8/version.py --- old/pyOpenSSL-0.7/version.py 2008-04-11 17:45:54.000000000 +0200 +++ new/pyOpenSSL-0.8/version.py 2008-10-19 18:01:32.000000000 +0200 @@ -5,4 +5,4 @@ pyOpenSSL - A simple wrapper around the OpenSSL library """ -__version__ = '0.7' +__version__ = '0.8' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Remember to have fun... -- To unsubscribe, e-mail: opensuse-commit+unsubscribe@opensuse.org For additional commands, e-mail: opensuse-commit+help@opensuse.org