From 43f65013961a2eeb0abb807474f9959d57b407a1 Mon Sep 17 00:00:00 2001 From: Kyungwook Tak Date: Fri, 16 Oct 2015 16:22:23 +0900 Subject: [PATCH] Imported Upstream version 0.14 Change-Id: Ic615e49cb2f1e1ba8a14cd9cea927bd5ffac3197 --- .gitignore | 3 + .travis.yml | 15 + ChangeLog | 68 + INSTALL | 137 +- MANIFEST.in | 5 +- OpenSSL/SSL.py | 1423 +++++++++++++ OpenSSL/__init__.py | 35 +- OpenSSL/_util.py | 53 + OpenSSL/crypto.py | 2314 ++++++++++++++++++++++ OpenSSL/crypto/crl.c | 290 --- OpenSSL/crypto/crl.h | 19 - OpenSSL/crypto/crypto.c | 898 --------- OpenSSL/crypto/crypto.h | 142 -- OpenSSL/crypto/netscape_spki.c | 316 --- OpenSSL/crypto/netscape_spki.h | 30 - OpenSSL/crypto/pkcs12.c | 580 ------ OpenSSL/crypto/pkcs12.h | 39 - OpenSSL/crypto/pkcs7.c | 216 -- OpenSSL/crypto/pkcs7.h | 30 - OpenSSL/crypto/pkey.c | 303 --- OpenSSL/crypto/pkey.h | 52 - OpenSSL/crypto/revoked.c | 444 ----- OpenSSL/crypto/revoked.h | 18 - OpenSSL/crypto/x509.c | 929 --------- OpenSSL/crypto/x509.h | 34 - OpenSSL/crypto/x509ext.c | 336 ---- OpenSSL/crypto/x509ext.h | 33 - OpenSSL/crypto/x509name.c | 555 ------ OpenSSL/crypto/x509name.h | 33 - OpenSSL/crypto/x509req.c | 431 ---- OpenSSL/crypto/x509req.h | 30 - OpenSSL/crypto/x509store.c | 149 -- OpenSSL/crypto/x509store.h | 30 - OpenSSL/py3k.h | 55 - OpenSSL/pymemcompat.h | 86 - OpenSSL/rand.py | 180 ++ OpenSSL/rand/rand.c | 306 --- OpenSSL/ssl/connection.c | 1581 --------------- OpenSSL/ssl/connection.h | 53 - OpenSSL/ssl/context.c | 1419 ------------- OpenSSL/ssl/context.h | 43 - OpenSSL/ssl/ssl.c | 293 --- OpenSSL/ssl/ssl.h | 76 - OpenSSL/test/README | 8 + OpenSSL/test/__init__.py | 2 +- OpenSSL/test/test_crypto.py | 961 ++++++--- OpenSSL/test/test_rand.py | 69 +- OpenSSL/test/test_ssl.py | 1165 +++++++++-- OpenSSL/test/util.py | 224 ++- OpenSSL/util.c | 96 - OpenSSL/util.h | 144 -- OpenSSL/version.py | 2 +- PKG-INFO | 15 - README | 5 +- dapper/README.Debian | 13 + dapper/changelog | 201 ++ dapper/control | 31 + dapper/copyright | 22 + dapper/pyopenssl-doc.doc-base | 13 + dapper/pyopenssl-doc.docs | 4 + dapper/pyopenssl-doc.examples | 1 + dapper/python-pyopenssl.docs | 1 + dapper/rules | 103 + doc/Makefile | 136 +- doc/Quotes | 2 + doc/README | 4 + doc/api.rst | 18 + doc/api/crypto.rst | 753 +++++++ doc/api/rand.rst | 79 + doc/api/ssl.rst | 773 ++++++++ doc/conf.py | 219 ++ doc/images/pyopenssl-brand.png | Bin 0 -> 3636 bytes doc/images/pyopenssl-icon.png | Bin 0 -> 428 bytes doc/images/pyopenssl-logo.png | Bin 0 -> 1483 bytes doc/images/pyopenssl.svg | 152 ++ doc/index.rst | 26 + doc/internals.rst | 69 + doc/introduction.rst | 17 + doc/make.bat | 170 ++ doc/pyOpenSSL.tex | 1451 -------------- doc/tools/anno-api.py | 71 - doc/tools/buildindex.py | 353 ---- doc/tools/checkargs.pm | 112 -- doc/tools/cklatex | 26 - doc/tools/custlib.py | 73 - doc/tools/cvsinfo.py | 81 - doc/tools/findacks | 161 -- doc/tools/findmodrefs | 63 - doc/tools/fix_hack | 2 - doc/tools/fix_libaux.sed | 3 - doc/tools/fixinfo.el | 15 - doc/tools/getpagecounts | 88 - doc/tools/html/about.dat | 24 - doc/tools/html/about.html | 74 - doc/tools/html/icons/blank.gif | Bin 1958 -> 0 bytes doc/tools/html/icons/blank.png | Bin 1031 -> 0 bytes doc/tools/html/icons/contents.gif | Bin 438 -> 0 bytes doc/tools/html/icons/contents.png | Bin 649 -> 0 bytes doc/tools/html/icons/index.gif | Bin 289 -> 0 bytes doc/tools/html/icons/index.png | Bin 529 -> 0 bytes doc/tools/html/icons/modules.gif | Bin 385 -> 0 bytes doc/tools/html/icons/modules.png | Bin 598 -> 0 bytes doc/tools/html/icons/next.gif | Bin 253 -> 0 bytes doc/tools/html/icons/next.png | Bin 511 -> 0 bytes doc/tools/html/icons/previous.gif | Bin 252 -> 0 bytes doc/tools/html/icons/previous.png | Bin 511 -> 0 bytes doc/tools/html/icons/up.gif | Bin 316 -> 0 bytes doc/tools/html/icons/up.png | Bin 577 -> 0 bytes doc/tools/html/index.html.in | 117 -- doc/tools/html/stdabout.dat | 48 - doc/tools/html/style.css | 88 - doc/tools/html2texi.pl | 1750 ---------------- doc/tools/indfix.py | 101 - doc/tools/info/Makefile | 73 - doc/tools/info/README | 21 - doc/tools/info/python.dir | 9 - doc/tools/keywords.py | 20 - doc/tools/listmodules | 183 -- doc/tools/mkackshtml | 65 - doc/tools/mkhowto | 597 ------ doc/tools/mkinfo | 48 - doc/tools/mkmodindex | 136 -- doc/tools/mksourcepkg | 163 -- doc/tools/node2label.pl | 55 - doc/tools/paper-a4/pypaper.sty | 5 - doc/tools/perl/SynopsisTable.pm | 89 - doc/tools/perl/distutils.perl | 21 - doc/tools/perl/howto.perl | 12 - doc/tools/perl/l2hinit.perl | 594 ------ doc/tools/perl/ltxmarkup.perl | 67 - doc/tools/perl/manual.perl | 15 - doc/tools/perl/python.perl | 1651 --------------- doc/tools/push-docs.sh | 42 - doc/tools/refcounts.py | 97 - doc/tools/sgmlconv/Makefile | 67 - doc/tools/sgmlconv/README | 58 - doc/tools/sgmlconv/conversion.xml | 757 ------- doc/tools/sgmlconv/docfixer.py | 1033 ---------- doc/tools/sgmlconv/esis2sgml.py | 263 --- doc/tools/sgmlconv/esistools.py | 309 --- doc/tools/sgmlconv/latex2esis.py | 555 ------ doc/tools/sgmlconv/make.rules | 48 - doc/tools/support.py | 149 -- doc/tools/templates/howto.tex | 105 - doc/tools/templates/manual.tex | 82 - doc/tools/templates/module.tex | 163 -- doc/tools/texinputs/boilerplate.tex | 10 - doc/tools/texinputs/copyright.tex | 108 - doc/tools/texinputs/distutils.sty | 33 - doc/tools/texinputs/fncychap.sty | 433 ---- doc/tools/texinputs/howto.cls | 106 - doc/tools/texinputs/ltxmarkup.sty | 40 - doc/tools/texinputs/manual.cls | 152 -- doc/tools/texinputs/pypaper.sty | 18 - doc/tools/texinputs/python.ist | 11 - doc/tools/texinputs/python.sty | 1082 ---------- doc/tools/texinputs/reportingbugs.tex | 65 - doc/tools/toc2bkm.py | 143 -- doc/tools/update-docs.sh | 21 - doc/tools/whichlibs | 2 - etch/README.Debian | 13 + etch/changelog | 189 ++ etch/control | 29 + etch/copyright | 22 + etch/pycompat | 1 + etch/pyopenssl-doc.doc-base | 13 + etch/pyopenssl-doc.docs | 4 + etch/pyopenssl-doc.examples | 1 + etch/python-pyopenssl.docs | 1 + etch/rules | 96 + gutsy/README.Debian | 13 + gutsy/changelog | 209 ++ gutsy/compat | 1 + gutsy/control | 40 + gutsy/copyright | 22 + gutsy/pycompat | 1 + gutsy/pyopenssl-doc.doc-base | 13 + gutsy/pyopenssl-doc.docs | 4 + gutsy/pyopenssl-doc.examples | 1 + gutsy/python-pyopenssl.docs | 1 + gutsy/rules | 102 + leakcheck/context-info-callback.py | 97 + leakcheck/context-passphrase-callback.py | 34 + leakcheck/context-verify-callback.py | 99 + leakcheck/crypto.py | 123 ++ leakcheck/dhparam.pem | 4 + leakcheck/thread-crash.py | 71 + leakcheck/thread-key-gen.py | 38 + memdbg.py | 82 + runtests.py | 11 + setup.cfg | 4 +- setup.py | 226 +-- tox.ini | 10 + 193 files changed, 10084 insertions(+), 25548 deletions(-) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 OpenSSL/SSL.py create mode 100644 OpenSSL/_util.py create mode 100644 OpenSSL/crypto.py delete mode 100644 OpenSSL/crypto/crl.c delete mode 100644 OpenSSL/crypto/crl.h delete mode 100644 OpenSSL/crypto/crypto.c delete mode 100644 OpenSSL/crypto/crypto.h delete mode 100644 OpenSSL/crypto/netscape_spki.c delete mode 100644 OpenSSL/crypto/netscape_spki.h delete mode 100644 OpenSSL/crypto/pkcs12.c delete mode 100644 OpenSSL/crypto/pkcs12.h delete mode 100644 OpenSSL/crypto/pkcs7.c delete mode 100644 OpenSSL/crypto/pkcs7.h delete mode 100644 OpenSSL/crypto/pkey.c delete mode 100644 OpenSSL/crypto/pkey.h delete mode 100644 OpenSSL/crypto/revoked.c delete mode 100644 OpenSSL/crypto/revoked.h delete mode 100644 OpenSSL/crypto/x509.c delete mode 100644 OpenSSL/crypto/x509.h delete mode 100644 OpenSSL/crypto/x509ext.c delete mode 100644 OpenSSL/crypto/x509ext.h delete mode 100644 OpenSSL/crypto/x509name.c delete mode 100644 OpenSSL/crypto/x509name.h delete mode 100644 OpenSSL/crypto/x509req.c delete mode 100644 OpenSSL/crypto/x509req.h delete mode 100644 OpenSSL/crypto/x509store.c delete mode 100644 OpenSSL/crypto/x509store.h delete mode 100644 OpenSSL/py3k.h delete mode 100644 OpenSSL/pymemcompat.h create mode 100644 OpenSSL/rand.py delete mode 100644 OpenSSL/rand/rand.c delete mode 100755 OpenSSL/ssl/connection.c delete mode 100644 OpenSSL/ssl/connection.h delete mode 100644 OpenSSL/ssl/context.c delete mode 100644 OpenSSL/ssl/context.h delete mode 100644 OpenSSL/ssl/ssl.c delete mode 100644 OpenSSL/ssl/ssl.h create mode 100644 OpenSSL/test/README delete mode 100644 OpenSSL/util.c delete mode 100644 OpenSSL/util.h delete mode 100644 PKG-INFO create mode 100644 dapper/README.Debian create mode 100644 dapper/changelog create mode 100644 dapper/control create mode 100644 dapper/copyright create mode 100644 dapper/pyopenssl-doc.doc-base create mode 100644 dapper/pyopenssl-doc.docs create mode 100644 dapper/pyopenssl-doc.examples create mode 100644 dapper/python-pyopenssl.docs create mode 100755 dapper/rules create mode 100644 doc/Quotes create mode 100644 doc/README create mode 100644 doc/api.rst create mode 100644 doc/api/crypto.rst create mode 100644 doc/api/rand.rst create mode 100644 doc/api/ssl.rst create mode 100644 doc/conf.py create mode 100644 doc/images/pyopenssl-brand.png create mode 100644 doc/images/pyopenssl-icon.png create mode 100644 doc/images/pyopenssl-logo.png create mode 100644 doc/images/pyopenssl.svg create mode 100644 doc/index.rst create mode 100644 doc/internals.rst create mode 100644 doc/introduction.rst create mode 100644 doc/make.bat delete mode 100644 doc/pyOpenSSL.tex delete mode 100755 doc/tools/anno-api.py delete mode 100755 doc/tools/buildindex.py delete mode 100644 doc/tools/checkargs.pm delete mode 100755 doc/tools/cklatex delete mode 100644 doc/tools/custlib.py delete mode 100644 doc/tools/cvsinfo.py delete mode 100755 doc/tools/findacks delete mode 100755 doc/tools/findmodrefs delete mode 100755 doc/tools/fix_hack delete mode 100755 doc/tools/fix_libaux.sed delete mode 100644 doc/tools/fixinfo.el delete mode 100755 doc/tools/getpagecounts delete mode 100644 doc/tools/html/about.dat delete mode 100644 doc/tools/html/about.html delete mode 100644 doc/tools/html/icons/blank.gif delete mode 100644 doc/tools/html/icons/blank.png delete mode 100644 doc/tools/html/icons/contents.gif delete mode 100644 doc/tools/html/icons/contents.png delete mode 100644 doc/tools/html/icons/index.gif delete mode 100644 doc/tools/html/icons/index.png delete mode 100644 doc/tools/html/icons/modules.gif delete mode 100644 doc/tools/html/icons/modules.png delete mode 100644 doc/tools/html/icons/next.gif delete mode 100644 doc/tools/html/icons/next.png delete mode 100644 doc/tools/html/icons/previous.gif delete mode 100644 doc/tools/html/icons/previous.png delete mode 100644 doc/tools/html/icons/up.gif delete mode 100644 doc/tools/html/icons/up.png delete mode 100644 doc/tools/html/index.html.in delete mode 100644 doc/tools/html/stdabout.dat delete mode 100644 doc/tools/html/style.css delete mode 100755 doc/tools/html2texi.pl delete mode 100755 doc/tools/indfix.py delete mode 100644 doc/tools/info/Makefile delete mode 100644 doc/tools/info/README delete mode 100644 doc/tools/info/python.dir delete mode 100644 doc/tools/keywords.py delete mode 100755 doc/tools/listmodules delete mode 100755 doc/tools/mkackshtml delete mode 100755 doc/tools/mkhowto delete mode 100755 doc/tools/mkinfo delete mode 100755 doc/tools/mkmodindex delete mode 100755 doc/tools/mksourcepkg delete mode 100755 doc/tools/node2label.pl delete mode 100644 doc/tools/paper-a4/pypaper.sty delete mode 100644 doc/tools/perl/SynopsisTable.pm delete mode 100644 doc/tools/perl/distutils.perl delete mode 100644 doc/tools/perl/howto.perl delete mode 100644 doc/tools/perl/l2hinit.perl delete mode 100644 doc/tools/perl/ltxmarkup.perl delete mode 100644 doc/tools/perl/manual.perl delete mode 100644 doc/tools/perl/python.perl delete mode 100755 doc/tools/push-docs.sh delete mode 100644 doc/tools/refcounts.py delete mode 100644 doc/tools/sgmlconv/Makefile delete mode 100644 doc/tools/sgmlconv/README delete mode 100644 doc/tools/sgmlconv/conversion.xml delete mode 100755 doc/tools/sgmlconv/docfixer.py delete mode 100755 doc/tools/sgmlconv/esis2sgml.py delete mode 100644 doc/tools/sgmlconv/esistools.py delete mode 100755 doc/tools/sgmlconv/latex2esis.py delete mode 100644 doc/tools/sgmlconv/make.rules delete mode 100644 doc/tools/support.py delete mode 100644 doc/tools/templates/howto.tex delete mode 100644 doc/tools/templates/manual.tex delete mode 100644 doc/tools/templates/module.tex delete mode 100644 doc/tools/texinputs/boilerplate.tex delete mode 100644 doc/tools/texinputs/copyright.tex delete mode 100644 doc/tools/texinputs/distutils.sty delete mode 100644 doc/tools/texinputs/fncychap.sty delete mode 100644 doc/tools/texinputs/howto.cls delete mode 100644 doc/tools/texinputs/ltxmarkup.sty delete mode 100644 doc/tools/texinputs/manual.cls delete mode 100644 doc/tools/texinputs/pypaper.sty delete mode 100644 doc/tools/texinputs/python.ist delete mode 100644 doc/tools/texinputs/python.sty delete mode 100644 doc/tools/texinputs/reportingbugs.tex delete mode 100755 doc/tools/toc2bkm.py delete mode 100755 doc/tools/update-docs.sh delete mode 100755 doc/tools/whichlibs create mode 100644 etch/README.Debian create mode 100644 etch/changelog create mode 100644 etch/control create mode 100644 etch/copyright create mode 100644 etch/pycompat create mode 100644 etch/pyopenssl-doc.doc-base create mode 100644 etch/pyopenssl-doc.docs create mode 100644 etch/pyopenssl-doc.examples create mode 100644 etch/python-pyopenssl.docs create mode 100644 etch/rules create mode 100644 gutsy/README.Debian create mode 100644 gutsy/changelog create mode 100644 gutsy/compat create mode 100644 gutsy/control create mode 100644 gutsy/copyright create mode 100644 gutsy/pycompat create mode 100644 gutsy/pyopenssl-doc.doc-base create mode 100644 gutsy/pyopenssl-doc.docs create mode 100644 gutsy/pyopenssl-doc.examples create mode 100644 gutsy/python-pyopenssl.docs create mode 100755 gutsy/rules create mode 100644 leakcheck/context-info-callback.py create mode 100644 leakcheck/context-passphrase-callback.py create mode 100644 leakcheck/context-verify-callback.py create mode 100644 leakcheck/crypto.py create mode 100644 leakcheck/dhparam.pem create mode 100644 leakcheck/thread-crash.py create mode 100644 leakcheck/thread-key-gen.py create mode 100644 memdbg.py create mode 100644 runtests.py create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..867434b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build +dist +*.egg-info \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..659677a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: python + +os: + - linux + - osx + +python: + - "pypy" + - "2.6" + - "2.7" + - "3.2" + - "3.3" + +script: + - python setup.py test diff --git a/ChangeLog b/ChangeLog index 4f92b03..95cf45b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,71 @@ +2014-01-09 Jean-Paul Calderone + + * OpenSSL: Port to the cffi-based OpenSSL bindings provided by + + +2013-10-06 Jean-Paul Calderone + + * OpenSSL/ssl/context.c: Add support for negotiating TLS v1.1 or + v1.2. + +2013-10-03 Christian Heimes + + * OpenSSL/crypto/x509.c: Fix an inconsistency in memory management + in X509.get_serial_number which leads to crashes on some runtimes + (certain Windows/Python 3.3 environments, at least). + +2013-08-11 Christian Heimes + + * OpenSSL/crypto/x509ext.c: Fix handling of NULL bytes inside + subjectAltName general names when formatting an X509 extension + as a string. + * OpenSSL/crypto/x509.c: Fix memory leak in get_extension(). + +2012-04-03 Jean-Paul Calderone + + * OpenSSL/crypto/pkey.c: Release the GIL around RSA and DSA key + generation, based on code from INADA Naoki. + +2012-02-13 Jean-Paul Calderone + + * OpenSSL/ssl/ssl.c: Add session cache related constants for use + with the new Context.set_session_cache_mode method. + + * OpenSSL/ssl/context.c: Add new Context methods + set_session_cache_mode and get_session_cache_mode. + +2011-11-01 Jean-Paul Calderone + + * OpenSSL/crypto/pkey.c: Raise TypeError when trying to check a + PKey instance which has no private component, instead of crashing. + Based on fix by . + +2011-09-14 Žiga Seilnacht + + * OpenSSL/crypto/crypto.c: Allow exceptions from passphrase + callbacks to propagate up out of load_privatekey + * OpenSSL/crypto/crypto.c: Raise an exception when a too-long + passphrase is returned from a passphrase callback, instead of + silently truncating it. + * OpenSSL/crypto/crypto.c: Fix a memory leak when a passphrase + callback returns the wrong type. + +2011-09-13 Jean-Paul Calderone + + * OpenSSL/crypto/crl.c: Add error handling for the use of + X509_CRL_sign. + +2011-09-11 Jonathan Ballet + + * doc/: Convert the LaTeX documentation to Sphinx-using ReST. + * OpenSSL/: Convert the epytext API documentation to Sphinx-using ReST. + +2011-09-08 Guillermo Gonzalez + + * OpenSSL/ssl/context.c: Add Context.set_mode method. + * OpenSSL/ssl/ssl.c: Add MODE_RELEASE_BUFFERS and OP_NO_COMPRESSION + constants. + 2011-09-02 Jean-Paul Calderone * Release 0.13 diff --git a/INSTALL b/INSTALL index f4635c5..3af722f 100644 --- a/INSTALL +++ b/INSTALL @@ -1,132 +1,27 @@ +Installation +------------ +pyOpenSSL uses distutils. Use setup.py to install it in the usual way: -INSTALLATION INSTRUCTIONS FOR pyOpenSSL ------------------------------------------------------------------------------- + $ python setup.py install --user -I have tested this on Debian Linux systems (woody and sid), Solaris 2.6 and -2.7. Others have successfully compiled it on Windows and NT. +Or use pip: - --- Building the Module on a Unix System -- - -pyOpenSSL uses distutils, so there really shouldn't be any problems. To build -the library: - - $ python setup.py build - -If your OpenSSL header files aren't in /usr/include, you may need to supply -the -I flag to let the setup script know where to look. The same goes for the -libraries of course, use the -L flag. Note that build won't accept these -flags, so you have to run first build_ext and then build! Example: - - $ python setup.py build_ext -I/usr/local/ssl/include -L/usr/local/ssl/lib - $ python setup.py build - -Now you should have a directory called OpenSSL that contains e.g. SSL.so and -__init__.py somewhere in the build dicrectory, so just: - - $ python setup.py install - -If you, for some arcane reason, don't want the module to appear in the -site-packages directory, use the --prefix option. + $ pip install --user . You can, of course, do $ python setup.py --help -to find out more about how to use the script. - - --- Building the Module on a Windows System -- - -First you should get OpenSSL linked with the same runtime library that Python -uses. If you are using Python 2.6 you can use the installer at: - - http://www.slproweb.com/products/Win32OpenSSL.html - -The binaries in the installer are built with Visual Studio 2008 at the -time of this writing, which is the same compiler used for building the -official Python 2.6 installers. - -If you want to build pyOpenSSL for an older Python version, it is preferred -to build OpenSSL yourself, either with the Visual Studio 2003 compiler or -with the MinGW compiler. This way you avoid all potential incompatibilities -between different versions of runtime library (msvcrt.dll). To build -OpenSSL follow the instructions in its source distribution and make sure -that you build a shared library, not a static one. pyOpenSSL fails some of -its tests when linked with the static OpenSSL libraries. Use the same -compiler for OpenSSL that you will use for pyOpenSSL later. Make sure that -OpenSSL is properly installed before continuing. To install OpenSSL when -building with MinGW, use the folowing script: - -set OPENSSL_INSTALL_DIR=%1 -mkdir %OPENSSL_INSTALL_DIR% -mkdir %OPENSSL_INSTALL_DIR%\bin -mkdir %OPENSSL_INSTALL_DIR%\include -mkdir %OPENSSL_INSTALL_DIR%\include\openssl -mkdir %OPENSSL_INSTALL_DIR%\lib -copy /b .\*.dll %OPENSSL_INSTALL_DIR%\bin -copy /b .\out\openssl.exe %OPENSSL_INSTALL_DIR%\bin -copy /b .\outinc\openssl\* %OPENSSL_INSTALL_DIR%\include\openssl -copy /b .\out\*.a %OPENSSL_INSTALL_DIR%\lib - -Ensure that OpenSSL's openssl.exe executable can be found on PATH before -running pyOpenSSL's setup script. The setup script finds OpenSSL's include -dir and lib dir based on the location of openssl.exe, and the test suite -requires openssl.exe for output comparison. Alternatively, you can specify -the --with-openssl option to setup.py's build_ext command with the path to -the OpenSSL installation dir: +or - > python setup.py build_ext --with-openssl=C:\path\to\openssl build + $ pip install --help -pyOpenSSL is known to build with mingw32 for Python 2.3 through Python 2.5. -Before using the mingw32 compiler for Python 2.3, you will have to create -a Python library that MinGW understands. Find and download the pexports -program, put it and MinGW's bin directory on path, then run from Python's -install dir: +to find out more about how to use these tools. -> pexports python23.dll > libs\python23.def -> dlltool --dllname python23.dll --def libs\python23.def \ - --output-lib libs\libpython23.a +Documentation +------------- -For Python 2.4 and 2.5, no special preparation is needed, just make sure that -MinGW's gcc is on PATH. You can specify that mingw32 be used by passing -the --compiler argument to build_ext: - - C:\pyOpenSSL-X.Y> setup.py build_ext -c mingw32 bdist_msi - -The bdist_msi command will build an MSI installer. It can be substituted -with another bdist command if another kind of installer is desired or with -the install command if you want to install directly. - -For Python 2.4 and 2.5 you can use Visual Studio 2003 in addition to MinGW. -For Python 2.6, the official Windows installer of which is built with -Microsoft Visual Studio 2008 (version 9.0), Microsoft Visual Studio 2008 -(version 9.0) is required. - -To build with MSVC, just omit the compiler specific option: - - C:\pyOpenSSL-X.Y> setup.py bdist_msi - -The resulting binary distribution will be placed in the dist directory. To -install it, depending on what kind of distribution you create, run it, -unzip it, or copy it to Python installation's site-packages. - -And similarily, you can do - - setup.py --help - -to get more information. - -Big thanks to Itamar Shtull-Trauring, Oleg Orlov, Zooko O'Whielacronx, Chris -Galvan, Žiga Seilnacht, and #python and #distutils on FreeNode for their -help with Windows build instructions and to Michael Schneider for providing -Windows build hosts. - --- Documentation -- - -The documentation is written in LaTeX, using the standard Python templates, -and tools to compile it into a number of forms are included. You need to -supply things like dvips, latex2html yourself of course! +The documentation is written in reStructuredText and build using Sphinx. To build the text, html, postscript or dvi forms of the documentation, this is what you do: @@ -136,11 +31,3 @@ what you do: make text # To make the dvi form: make dvi - -It's as simple as that. Note that since Python's mkhowto script is used, if -you do first ``make dvi'' and then ``make ps'', the dvi file will disappear. -I included a special build target ``make all'' that will build all the -documentation in an order that won't let anything disappear. - - -@(#) $Id: INSTALL,v 1.7 2002/06/14 12:14:19 martin Exp $ diff --git a/MANIFEST.in b/MANIFEST.in index 0c2be95..8137258 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,6 @@ -recursive-include OpenSSL *.h include LICENSE ChangeLog INSTALL README TODO MANIFEST.in OpenSSL/RATIONALE -include doc/pyOpenSSL.tex doc/Makefile -recursive-include doc/tools * +recursive-include doc * recursive-include examples * recursive-include rpm * global-exclude *.pyc +prune doc/_build diff --git a/OpenSSL/SSL.py b/OpenSSL/SSL.py new file mode 100644 index 0000000..a257f16 --- /dev/null +++ b/OpenSSL/SSL.py @@ -0,0 +1,1423 @@ +from sys import platform +from functools import wraps, partial +from itertools import count +from weakref import WeakValueDictionary +from errno import errorcode + +from six import text_type as _text_type +from six import integer_types as integer_types + +from OpenSSL._util import ( + ffi as _ffi, + lib as _lib, + exception_from_error_queue as _exception_from_error_queue, + native as _native) + +from OpenSSL.crypto import ( + FILETYPE_PEM, _PassphraseHelper, PKey, X509Name, X509, X509Store) + +_unspecified = object() + +try: + _memoryview = memoryview +except NameError: + class _memoryview(object): + pass + +OPENSSL_VERSION_NUMBER = _lib.OPENSSL_VERSION_NUMBER +SSLEAY_VERSION = _lib.SSLEAY_VERSION +SSLEAY_CFLAGS = _lib.SSLEAY_CFLAGS +SSLEAY_PLATFORM = _lib.SSLEAY_PLATFORM +SSLEAY_DIR = _lib.SSLEAY_DIR +SSLEAY_BUILT_ON = _lib.SSLEAY_BUILT_ON + +SENT_SHUTDOWN = _lib.SSL_SENT_SHUTDOWN +RECEIVED_SHUTDOWN = _lib.SSL_RECEIVED_SHUTDOWN + +SSLv2_METHOD = 1 +SSLv3_METHOD = 2 +SSLv23_METHOD = 3 +TLSv1_METHOD = 4 +TLSv1_1_METHOD = 5 +TLSv1_2_METHOD = 6 + +OP_NO_SSLv2 = _lib.SSL_OP_NO_SSLv2 +OP_NO_SSLv3 = _lib.SSL_OP_NO_SSLv3 +OP_NO_TLSv1 = _lib.SSL_OP_NO_TLSv1 + +OP_NO_TLSv1_1 = getattr(_lib, "SSL_OP_NO_TLSv1_1", 0) +OP_NO_TLSv1_2 = getattr(_lib, "SSL_OP_NO_TLSv1_2", 0) + +try: + MODE_RELEASE_BUFFERS = _lib.SSL_MODE_RELEASE_BUFFERS +except AttributeError: + pass + +OP_SINGLE_DH_USE = _lib.SSL_OP_SINGLE_DH_USE +OP_EPHEMERAL_RSA = _lib.SSL_OP_EPHEMERAL_RSA +OP_MICROSOFT_SESS_ID_BUG = _lib.SSL_OP_MICROSOFT_SESS_ID_BUG +OP_NETSCAPE_CHALLENGE_BUG = _lib.SSL_OP_NETSCAPE_CHALLENGE_BUG +OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = _lib.SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG +OP_SSLREF2_REUSE_CERT_TYPE_BUG = _lib.SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG +OP_MICROSOFT_BIG_SSLV3_BUFFER = _lib.SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER +try: + OP_MSIE_SSLV2_RSA_PADDING = _lib.SSL_OP_MSIE_SSLV2_RSA_PADDING +except AttributeError: + pass +OP_SSLEAY_080_CLIENT_DH_BUG = _lib.SSL_OP_SSLEAY_080_CLIENT_DH_BUG +OP_TLS_D5_BUG = _lib.SSL_OP_TLS_D5_BUG +OP_TLS_BLOCK_PADDING_BUG = _lib.SSL_OP_TLS_BLOCK_PADDING_BUG +OP_DONT_INSERT_EMPTY_FRAGMENTS = _lib.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS +OP_CIPHER_SERVER_PREFERENCE = _lib.SSL_OP_CIPHER_SERVER_PREFERENCE +OP_TLS_ROLLBACK_BUG = _lib.SSL_OP_TLS_ROLLBACK_BUG +OP_PKCS1_CHECK_1 = _lib.SSL_OP_PKCS1_CHECK_1 +OP_PKCS1_CHECK_2 = _lib.SSL_OP_PKCS1_CHECK_2 +OP_NETSCAPE_CA_DN_BUG = _lib.SSL_OP_NETSCAPE_CA_DN_BUG +OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG= _lib.SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG +try: + OP_NO_COMPRESSION = _lib.SSL_OP_NO_COMPRESSION +except AttributeError: + pass + +OP_NO_QUERY_MTU = _lib.SSL_OP_NO_QUERY_MTU +OP_COOKIE_EXCHANGE = _lib.SSL_OP_COOKIE_EXCHANGE +OP_NO_TICKET = _lib.SSL_OP_NO_TICKET + +OP_ALL = _lib.SSL_OP_ALL + +VERIFY_PEER = _lib.SSL_VERIFY_PEER +VERIFY_FAIL_IF_NO_PEER_CERT = _lib.SSL_VERIFY_FAIL_IF_NO_PEER_CERT +VERIFY_CLIENT_ONCE = _lib.SSL_VERIFY_CLIENT_ONCE +VERIFY_NONE = _lib.SSL_VERIFY_NONE + +SESS_CACHE_OFF = _lib.SSL_SESS_CACHE_OFF +SESS_CACHE_CLIENT = _lib.SSL_SESS_CACHE_CLIENT +SESS_CACHE_SERVER = _lib.SSL_SESS_CACHE_SERVER +SESS_CACHE_BOTH = _lib.SSL_SESS_CACHE_BOTH +SESS_CACHE_NO_AUTO_CLEAR = _lib.SSL_SESS_CACHE_NO_AUTO_CLEAR +SESS_CACHE_NO_INTERNAL_LOOKUP = _lib.SSL_SESS_CACHE_NO_INTERNAL_LOOKUP +SESS_CACHE_NO_INTERNAL_STORE = _lib.SSL_SESS_CACHE_NO_INTERNAL_STORE +SESS_CACHE_NO_INTERNAL = _lib.SSL_SESS_CACHE_NO_INTERNAL + +SSL_ST_CONNECT = _lib.SSL_ST_CONNECT +SSL_ST_ACCEPT = _lib.SSL_ST_ACCEPT +SSL_ST_MASK = _lib.SSL_ST_MASK +SSL_ST_INIT = _lib.SSL_ST_INIT +SSL_ST_BEFORE = _lib.SSL_ST_BEFORE +SSL_ST_OK = _lib.SSL_ST_OK +SSL_ST_RENEGOTIATE = _lib.SSL_ST_RENEGOTIATE + +SSL_CB_LOOP = _lib.SSL_CB_LOOP +SSL_CB_EXIT = _lib.SSL_CB_EXIT +SSL_CB_READ = _lib.SSL_CB_READ +SSL_CB_WRITE = _lib.SSL_CB_WRITE +SSL_CB_ALERT = _lib.SSL_CB_ALERT +SSL_CB_READ_ALERT = _lib.SSL_CB_READ_ALERT +SSL_CB_WRITE_ALERT = _lib.SSL_CB_WRITE_ALERT +SSL_CB_ACCEPT_LOOP = _lib.SSL_CB_ACCEPT_LOOP +SSL_CB_ACCEPT_EXIT = _lib.SSL_CB_ACCEPT_EXIT +SSL_CB_CONNECT_LOOP = _lib.SSL_CB_CONNECT_LOOP +SSL_CB_CONNECT_EXIT = _lib.SSL_CB_CONNECT_EXIT +SSL_CB_HANDSHAKE_START = _lib.SSL_CB_HANDSHAKE_START +SSL_CB_HANDSHAKE_DONE = _lib.SSL_CB_HANDSHAKE_DONE + + +class Error(Exception): + """ + An error occurred in an `OpenSSL.SSL` API. + """ + + + +_raise_current_error = partial(_exception_from_error_queue, Error) + + +class WantReadError(Error): + pass + + + +class WantWriteError(Error): + pass + + + +class WantX509LookupError(Error): + pass + + + +class ZeroReturnError(Error): + pass + + + +class SysCallError(Error): + pass + + + +class _VerifyHelper(object): + def __init__(self, connection, callback): + self._problems = [] + + @wraps(callback) + def wrapper(ok, store_ctx): + cert = X509.__new__(X509) + cert._x509 = _lib.X509_STORE_CTX_get_current_cert(store_ctx) + error_number = _lib.X509_STORE_CTX_get_error(store_ctx) + error_depth = _lib.X509_STORE_CTX_get_error_depth(store_ctx) + + try: + result = callback(connection, cert, error_number, error_depth, ok) + except Exception as e: + self._problems.append(e) + return 0 + else: + if result: + _lib.X509_STORE_CTX_set_error(store_ctx, _lib.X509_V_OK) + return 1 + else: + return 0 + + self.callback = _ffi.callback( + "int (*)(int, X509_STORE_CTX *)", wrapper) + + + def raise_if_problem(self): + if self._problems: + try: + _raise_current_error() + except Error: + pass + raise self._problems.pop(0) + + + +def _asFileDescriptor(obj): + fd = None + if not isinstance(obj, integer_types): + meth = getattr(obj, "fileno", None) + if meth is not None: + obj = meth() + + if isinstance(obj, integer_types): + fd = obj + + if not isinstance(fd, integer_types): + raise TypeError("argument must be an int, or have a fileno() method.") + elif fd < 0: + raise ValueError( + "file descriptor cannot be a negative integer (%i)" % (fd,)) + + return fd + + + +def SSLeay_version(type): + """ + Return a string describing the version of OpenSSL in use. + + :param type: One of the SSLEAY_ constants defined in this module. + """ + return _ffi.string(_lib.SSLeay_version(type)) + + + +class Session(object): + pass + + + +class Context(object): + """ + :py:obj:`OpenSSL.SSL.Context` instances define the parameters for setting up + new SSL connections. + """ + _methods = { + SSLv3_METHOD: "SSLv3_method", + SSLv23_METHOD: "SSLv23_method", + TLSv1_METHOD: "TLSv1_method", + TLSv1_1_METHOD: "TLSv1_1_method", + TLSv1_2_METHOD: "TLSv1_2_method", + } + _methods = dict( + (identifier, getattr(_lib, name)) + for (identifier, name) in _methods.items() + if getattr(_lib, name, None) is not None) + + + def __init__(self, method): + """ + :param method: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, or + TLSv1_METHOD. + """ + if not isinstance(method, integer_types): + raise TypeError("method must be an integer") + + try: + method_func = self._methods[method] + except KeyError: + raise ValueError("No such protocol") + + method_obj = method_func() + if method_obj == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + context = _lib.SSL_CTX_new(method_obj) + if context == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + context = _ffi.gc(context, _lib.SSL_CTX_free) + + self._context = context + self._passphrase_helper = None + self._passphrase_callback = None + self._passphrase_userdata = None + self._verify_helper = None + self._verify_callback = None + self._info_callback = None + self._tlsext_servername_callback = None + self._app_data = None + + # SSL_CTX_set_app_data(self->ctx, self); + # SSL_CTX_set_mode(self->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | + # SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | + # SSL_MODE_AUTO_RETRY); + self.set_mode(_lib.SSL_MODE_ENABLE_PARTIAL_WRITE) + + + def load_verify_locations(self, cafile, capath=None): + """ + Let SSL know where we can find trusted certificates for the certificate + chain + + :param cafile: In which file we can find the certificates + :param capath: In which directory we can find the certificates + :return: None + """ + if cafile is None: + cafile = _ffi.NULL + elif not isinstance(cafile, bytes): + raise TypeError("cafile must be None or a byte string") + + if capath is None: + capath = _ffi.NULL + elif not isinstance(capath, bytes): + raise TypeError("capath must be None or a byte string") + + load_result = _lib.SSL_CTX_load_verify_locations(self._context, cafile, capath) + if not load_result: + _raise_current_error() + + + def _wrap_callback(self, callback): + @wraps(callback) + def wrapper(size, verify, userdata): + return callback(size, verify, self._passphrase_userdata) + return _PassphraseHelper( + FILETYPE_PEM, wrapper, more_args=True, truncate=True) + + + def set_passwd_cb(self, callback, userdata=None): + """ + Set the passphrase callback + + :param callback: The Python callback to use + :param userdata: (optional) A Python object which will be given as + argument to the callback + :return: None + """ + if not callable(callback): + raise TypeError("callback must be callable") + + self._passphrase_helper = self._wrap_callback(callback) + self._passphrase_callback = self._passphrase_helper.callback + _lib.SSL_CTX_set_default_passwd_cb( + self._context, self._passphrase_callback) + self._passphrase_userdata = userdata + + + def set_default_verify_paths(self): + """ + Use the platform-specific CA certificate locations + + :return: None + """ + set_result = _lib.SSL_CTX_set_default_verify_paths(self._context) + if not set_result: + # TODO: This is untested. + _raise_current_error() + + + def use_certificate_chain_file(self, certfile): + """ + Load a certificate chain from a file + + :param certfile: The name of the certificate chain file + :return: None + """ + if isinstance(certfile, _text_type): + # Perhaps sys.getfilesystemencoding() could be better? + certfile = certfile.encode("utf-8") + + if not isinstance(certfile, bytes): + raise TypeError("certfile must be bytes or unicode") + + result = _lib.SSL_CTX_use_certificate_chain_file(self._context, certfile) + if not result: + _raise_current_error() + + + def use_certificate_file(self, certfile, filetype=FILETYPE_PEM): + """ + Load a certificate from a file + + :param certfile: The name of the certificate file + :param filetype: (optional) The encoding of the file, default is PEM + :return: None + """ + if isinstance(certfile, _text_type): + # Perhaps sys.getfilesystemencoding() could be better? + certfile = certfile.encode("utf-8") + if not isinstance(certfile, bytes): + raise TypeError("certfile must be bytes or unicode") + if not isinstance(filetype, integer_types): + raise TypeError("filetype must be an integer") + + use_result = _lib.SSL_CTX_use_certificate_file(self._context, certfile, filetype) + if not use_result: + _raise_current_error() + + + def use_certificate(self, cert): + """ + Load a certificate from a X509 object + + :param cert: The X509 object + :return: None + """ + if not isinstance(cert, X509): + raise TypeError("cert must be an X509 instance") + + use_result = _lib.SSL_CTX_use_certificate(self._context, cert._x509) + if not use_result: + _raise_current_error() + + + def add_extra_chain_cert(self, certobj): + """ + Add certificate to chain + + :param certobj: The X509 certificate object to add to the chain + :return: None + """ + if not isinstance(certobj, X509): + raise TypeError("certobj must be an X509 instance") + + copy = _lib.X509_dup(certobj._x509) + add_result = _lib.SSL_CTX_add_extra_chain_cert(self._context, copy) + if not add_result: + # TODO: This is untested. + _lib.X509_free(copy) + _raise_current_error() + + + def _raise_passphrase_exception(self): + if self._passphrase_helper is None: + _raise_current_error() + exception = self._passphrase_helper.raise_if_problem(Error) + if exception is not None: + raise exception + + + def use_privatekey_file(self, keyfile, filetype=_unspecified): + """ + Load a private key from a file + + :param keyfile: The name of the key file + :param filetype: (optional) The encoding of the file, default is PEM + :return: None + """ + if isinstance(keyfile, _text_type): + # Perhaps sys.getfilesystemencoding() could be better? + keyfile = keyfile.encode("utf-8") + + if not isinstance(keyfile, bytes): + raise TypeError("keyfile must be a byte string") + + if filetype is _unspecified: + filetype = FILETYPE_PEM + elif not isinstance(filetype, integer_types): + raise TypeError("filetype must be an integer") + + use_result = _lib.SSL_CTX_use_PrivateKey_file( + self._context, keyfile, filetype) + if not use_result: + self._raise_passphrase_exception() + + + def use_privatekey(self, pkey): + """ + Load a private key from a PKey object + + :param pkey: The PKey object + :return: None + """ + if not isinstance(pkey, PKey): + raise TypeError("pkey must be a PKey instance") + + use_result = _lib.SSL_CTX_use_PrivateKey(self._context, pkey._pkey) + if not use_result: + self._raise_passphrase_exception() + + + def check_privatekey(self): + """ + Check that the private key and certificate match up + + :return: None (raises an exception if something's wrong) + """ + + def load_client_ca(self, cafile): + """ + Load the trusted certificates that will be sent to the client (basically + telling the client "These are the guys I trust"). Does not actually + imply any of the certificates are trusted; that must be configured + separately. + + :param cafile: The name of the certificates file + :return: None + """ + + def set_session_id(self, buf): + """ + Set the session identifier. This is needed if you want to do session + resumption. + + :param buf: A Python object that can be safely converted to a string + :returns: None + """ + + def set_session_cache_mode(self, mode): + """ + Enable/disable session caching and specify the mode used. + + :param mode: One or more of the SESS_CACHE_* flags (combine using + bitwise or) + :returns: The previously set caching mode. + """ + if not isinstance(mode, integer_types): + raise TypeError("mode must be an integer") + + return _lib.SSL_CTX_set_session_cache_mode(self._context, mode) + + + def get_session_cache_mode(self): + """ + :returns: The currently used cache mode. + """ + return _lib.SSL_CTX_get_session_cache_mode(self._context) + + + def set_verify(self, mode, callback): + """ + Set the verify mode and verify callback + + :param mode: The verify mode, this is either VERIFY_NONE or + VERIFY_PEER combined with possible other flags + :param callback: The Python callback to use + :return: None + + See SSL_CTX_set_verify(3SSL) for further details. + """ + if not isinstance(mode, integer_types): + raise TypeError("mode must be an integer") + + if not callable(callback): + raise TypeError("callback must be callable") + + self._verify_helper = _VerifyHelper(self, callback) + self._verify_callback = self._verify_helper.callback + _lib.SSL_CTX_set_verify(self._context, mode, self._verify_callback) + + + def set_verify_depth(self, depth): + """ + Set the verify depth + + :param depth: An integer specifying the verify depth + :return: None + """ + if not isinstance(depth, integer_types): + raise TypeError("depth must be an integer") + + _lib.SSL_CTX_set_verify_depth(self._context, depth) + + + def get_verify_mode(self): + """ + Get the verify mode + + :return: The verify mode + """ + return _lib.SSL_CTX_get_verify_mode(self._context) + + + def get_verify_depth(self): + """ + Get the verify depth + + :return: The verify depth + """ + return _lib.SSL_CTX_get_verify_depth(self._context) + + + def load_tmp_dh(self, dhfile): + """ + Load parameters for Ephemeral Diffie-Hellman + + :param dhfile: The file to load EDH parameters from + :return: None + """ + if not isinstance(dhfile, bytes): + raise TypeError("dhfile must be a byte string") + + bio = _lib.BIO_new_file(dhfile, b"r") + if bio == _ffi.NULL: + _raise_current_error() + bio = _ffi.gc(bio, _lib.BIO_free) + + dh = _lib.PEM_read_bio_DHparams(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL) + dh = _ffi.gc(dh, _lib.DH_free) + _lib.SSL_CTX_set_tmp_dh(self._context, dh) + + + def set_cipher_list(self, cipher_list): + """ + Change the cipher list + + :param cipher_list: A cipher list, see ciphers(1) + :return: None + """ + if isinstance(cipher_list, _text_type): + cipher_list = cipher_list.encode("ascii") + + if not isinstance(cipher_list, bytes): + raise TypeError("cipher_list must be bytes or unicode") + + result = _lib.SSL_CTX_set_cipher_list(self._context, cipher_list) + if not result: + _raise_current_error() + + + def set_client_ca_list(self, certificate_authorities): + """ + Set the list of preferred client certificate signers for this server context. + + This list of certificate authorities will be sent to the client when the + server requests a client certificate. + + :param certificate_authorities: a sequence of X509Names. + :return: None + """ + name_stack = _lib.sk_X509_NAME_new_null() + if name_stack == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + try: + for ca_name in certificate_authorities: + if not isinstance(ca_name, X509Name): + raise TypeError( + "client CAs must be X509Name objects, not %s objects" % ( + type(ca_name).__name__,)) + copy = _lib.X509_NAME_dup(ca_name._name) + if copy == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + push_result = _lib.sk_X509_NAME_push(name_stack, copy) + if not push_result: + _lib.X509_NAME_free(copy) + _raise_current_error() + except: + _lib.sk_X509_NAME_free(name_stack) + raise + + _lib.SSL_CTX_set_client_CA_list(self._context, name_stack) + + + def add_client_ca(self, certificate_authority): + """ + Add the CA certificate to the list of preferred signers for this context. + + The list of certificate authorities will be sent to the client when the + server requests a client certificate. + + :param certificate_authority: certificate authority's X509 certificate. + :return: None + """ + if not isinstance(certificate_authority, X509): + raise TypeError("certificate_authority must be an X509 instance") + + add_result = _lib.SSL_CTX_add_client_CA( + self._context, certificate_authority._x509) + if not add_result: + # TODO: This is untested. + _raise_current_error() + + + def set_timeout(self, timeout): + """ + Set session timeout + + :param timeout: The timeout in seconds + :return: The previous session timeout + """ + if not isinstance(timeout, integer_types): + raise TypeError("timeout must be an integer") + + return _lib.SSL_CTX_set_timeout(self._context, timeout) + + + def get_timeout(self): + """ + Get the session timeout + + :return: The session timeout + """ + return _lib.SSL_CTX_get_timeout(self._context) + + + def set_info_callback(self, callback): + """ + Set the info callback + + :param callback: The Python callback to use + :return: None + """ + @wraps(callback) + def wrapper(ssl, where, return_code): + callback(Connection._reverse_mapping[ssl], where, return_code) + self._info_callback = _ffi.callback( + "void (*)(const SSL *, int, int)", wrapper) + _lib.SSL_CTX_set_info_callback(self._context, self._info_callback) + + + def get_app_data(self): + """ + Get the application data (supplied via set_app_data()) + + :return: The application data + """ + return self._app_data + + + def set_app_data(self, data): + """ + Set the application data (will be returned from get_app_data()) + + :param data: Any Python object + :return: None + """ + self._app_data = data + + + def get_cert_store(self): + """ + Get the certificate store for the context. + + :return: A X509Store object or None if it does not have one. + """ + store = _lib.SSL_CTX_get_cert_store(self._context) + if store == _ffi.NULL: + # TODO: This is untested. + return None + + pystore = X509Store.__new__(X509Store) + pystore._store = store + return pystore + + + def set_options(self, options): + """ + Add options. Options set before are not cleared! + + :param options: The options to add. + :return: The new option bitmask. + """ + if not isinstance(options, integer_types): + raise TypeError("options must be an integer") + + return _lib.SSL_CTX_set_options(self._context, options) + + + def set_mode(self, mode): + """ + Add modes via bitmask. Modes set before are not cleared! + + :param mode: The mode to add. + :return: The new mode bitmask. + """ + if not isinstance(mode, integer_types): + raise TypeError("mode must be an integer") + + return _lib.SSL_CTX_set_mode(self._context, mode) + + + def set_tlsext_servername_callback(self, callback): + """ + Specify a callback function to be called when clients specify a server name. + + :param callback: The callback function. It will be invoked with one + argument, the Connection instance. + """ + @wraps(callback) + def wrapper(ssl, alert, arg): + callback(Connection._reverse_mapping[ssl]) + return 0 + + self._tlsext_servername_callback = _ffi.callback( + "int (*)(const SSL *, int *, void *)", wrapper) + _lib.SSL_CTX_set_tlsext_servername_callback( + self._context, self._tlsext_servername_callback) + +ContextType = Context + + + +class Connection(object): + """ + """ + _reverse_mapping = WeakValueDictionary() + + def __init__(self, context, socket=None): + """ + Create a new Connection object, using the given OpenSSL.SSL.Context + instance and socket. + + :param context: An SSL Context to use for this connection + :param socket: The socket to use for transport layer + """ + if not isinstance(context, Context): + raise TypeError("context must be a Context instance") + + ssl = _lib.SSL_new(context._context) + self._ssl = _ffi.gc(ssl, _lib.SSL_free) + self._context = context + + self._reverse_mapping[self._ssl] = self + + if socket is None: + self._socket = None + # Don't set up any gc for these, SSL_free will take care of them. + self._into_ssl = _lib.BIO_new(_lib.BIO_s_mem()) + self._from_ssl = _lib.BIO_new(_lib.BIO_s_mem()) + + if self._into_ssl == _ffi.NULL or self._from_ssl == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + _lib.SSL_set_bio(self._ssl, self._into_ssl, self._from_ssl) + else: + self._into_ssl = None + self._from_ssl = None + self._socket = socket + set_result = _lib.SSL_set_fd(self._ssl, _asFileDescriptor(self._socket)) + if not set_result: + # TODO: This is untested. + _raise_current_error() + + + def __getattr__(self, name): + """ + Look up attributes on the wrapped socket object if they are not found on + the Connection object. + """ + return getattr(self._socket, name) + + + def _raise_ssl_error(self, ssl, result): + if self._context._verify_helper is not None: + self._context._verify_helper.raise_if_problem() + + error = _lib.SSL_get_error(ssl, result) + if error == _lib.SSL_ERROR_WANT_READ: + raise WantReadError() + elif error == _lib.SSL_ERROR_WANT_WRITE: + raise WantWriteError() + elif error == _lib.SSL_ERROR_ZERO_RETURN: + raise ZeroReturnError() + elif error == _lib.SSL_ERROR_WANT_X509_LOOKUP: + # TODO: This is untested. + raise WantX509LookupError() + elif error == _lib.SSL_ERROR_SYSCALL: + if _lib.ERR_peek_error() == 0: + if result < 0: + if platform == "win32": + errno = _ffi.getwinerror()[0] + else: + errno = _ffi.errno + raise SysCallError(errno, errorcode[errno]) + else: + raise SysCallError(-1, "Unexpected EOF") + else: + # TODO: This is untested. + _raise_current_error() + elif error == _lib.SSL_ERROR_NONE: + pass + else: + _raise_current_error() + + + def get_context(self): + """ + Get session context + """ + return self._context + + + def set_context(self, context): + """ + Switch this connection to a new session context + + :param context: A :py:class:`Context` instance giving the new session + context to use. + """ + if not isinstance(context, Context): + raise TypeError("context must be a Context instance") + + _lib.SSL_set_SSL_CTX(self._ssl, context._context) + self._context = context + + + def get_servername(self): + """ + Retrieve the servername extension value if provided in the client hello + message, or None if there wasn't one. + + :return: A byte string giving the server name or :py:data:`None`. + """ + name = _lib.SSL_get_servername(self._ssl, _lib.TLSEXT_NAMETYPE_host_name) + if name == _ffi.NULL: + return None + + return _ffi.string(name) + + + def set_tlsext_host_name(self, name): + """ + Set the value of the servername extension to send in the client hello. + + :param name: A byte string giving the name. + """ + if not isinstance(name, bytes): + raise TypeError("name must be a byte string") + elif b"\0" in name: + raise TypeError("name must not contain NUL byte") + + # XXX I guess this can fail sometimes? + _lib.SSL_set_tlsext_host_name(self._ssl, name) + + + def pending(self): + """ + Get the number of bytes that can be safely read from the connection + + :return: The number of bytes available in the receive buffer. + """ + return _lib.SSL_pending(self._ssl) + + + def send(self, buf, flags=0): + """ + Send data on the connection. NOTE: If you get one of the WantRead, + WantWrite or WantX509Lookup exceptions on this, you have to call the + method again with the SAME buffer. + + :param buf: The string to send + :param flags: (optional) Included for compatibility with the socket + API, the value is ignored + :return: The number of bytes written + """ + if isinstance(buf, _memoryview): + buf = buf.tobytes() + if not isinstance(buf, bytes): + raise TypeError("data must be a byte string") + + result = _lib.SSL_write(self._ssl, buf, len(buf)) + self._raise_ssl_error(self._ssl, result) + return result + write = send + + + def sendall(self, buf, flags=0): + """ + Send "all" data on the connection. This calls send() repeatedly until + all data is sent. If an error occurs, it's impossible to tell how much + data has been sent. + + :param buf: The string to send + :param flags: (optional) Included for compatibility with the socket + API, the value is ignored + :return: The number of bytes written + """ + if isinstance(buf, _memoryview): + buf = buf.tobytes() + if not isinstance(buf, bytes): + raise TypeError("buf must be a byte string") + + left_to_send = len(buf) + total_sent = 0 + data = _ffi.new("char[]", buf) + + while left_to_send: + result = _lib.SSL_write(self._ssl, data + total_sent, left_to_send) + self._raise_ssl_error(self._ssl, result) + total_sent += result + left_to_send -= result + + + def recv(self, bufsiz, flags=None): + """ + Receive data on the connection. NOTE: If you get one of the WantRead, + WantWrite or WantX509Lookup exceptions on this, you have to call the + method again with the SAME buffer. + + :param bufsiz: The maximum number of bytes to read + :param flags: (optional) Included for compatibility with the socket + API, the value is ignored + :return: The string read from the Connection + """ + buf = _ffi.new("char[]", bufsiz) + result = _lib.SSL_read(self._ssl, buf, bufsiz) + self._raise_ssl_error(self._ssl, result) + return _ffi.buffer(buf, result)[:] + read = recv + + + def _handle_bio_errors(self, bio, result): + if _lib.BIO_should_retry(bio): + if _lib.BIO_should_read(bio): + raise WantReadError() + elif _lib.BIO_should_write(bio): + # TODO: This is untested. + raise WantWriteError() + elif _lib.BIO_should_io_special(bio): + # TODO: This is untested. I think io_special means the socket + # BIO has a not-yet connected socket. + raise ValueError("BIO_should_io_special") + else: + # TODO: This is untested. + raise ValueError("unknown bio failure") + else: + # TODO: This is untested. + _raise_current_error() + + + def bio_read(self, bufsiz): + """ + When using non-socket connections this function reads the "dirty" data + that would have traveled away on the network. + + :param bufsiz: The maximum number of bytes to read + :return: The string read. + """ + if self._from_ssl is None: + raise TypeError("Connection sock was not None") + + if not isinstance(bufsiz, integer_types): + raise TypeError("bufsiz must be an integer") + + buf = _ffi.new("char[]", bufsiz) + result = _lib.BIO_read(self._from_ssl, buf, bufsiz) + if result <= 0: + self._handle_bio_errors(self._from_ssl, result) + + return _ffi.buffer(buf, result)[:] + + + def bio_write(self, buf): + """ + When using non-socket connections this function sends "dirty" data that + would have traveled in on the network. + + :param buf: The string to put into the memory BIO. + :return: The number of bytes written + """ + if self._into_ssl is None: + raise TypeError("Connection sock was not None") + + if not isinstance(buf, bytes): + raise TypeError("buf must be a byte string") + + result = _lib.BIO_write(self._into_ssl, buf, len(buf)) + if result <= 0: + self._handle_bio_errors(self._into_ssl, result) + return result + + + def renegotiate(self): + """ + Renegotiate the session + + :return: True if the renegotiation can be started, false otherwise + """ + + def do_handshake(self): + """ + Perform an SSL handshake (usually called after renegotiate() or one of + set_*_state()). This can raise the same exceptions as send and recv. + + :return: None. + """ + result = _lib.SSL_do_handshake(self._ssl) + self._raise_ssl_error(self._ssl, result) + + + def renegotiate_pending(self): + """ + Check if there's a renegotiation in progress, it will return false once + a renegotiation is finished. + + :return: Whether there's a renegotiation in progress + """ + + def total_renegotiations(self): + """ + Find out the total number of renegotiations. + + :return: The number of renegotiations. + """ + return _lib.SSL_total_renegotiations(self._ssl) + + + def connect(self, addr): + """ + Connect to remote host and set up client-side SSL + + :param addr: A remote address + :return: What the socket's connect method returns + """ + _lib.SSL_set_connect_state(self._ssl) + return self._socket.connect(addr) + + + def connect_ex(self, addr): + """ + Connect to remote host and set up client-side SSL. Note that if the socket's + connect_ex method doesn't return 0, SSL won't be initialized. + + :param addr: A remove address + :return: What the socket's connect_ex method returns + """ + connect_ex = self._socket.connect_ex + self.set_connect_state() + return connect_ex(addr) + + + def accept(self): + """ + Accept incoming connection and set up SSL on it + + :return: A (conn,addr) pair where conn is a Connection and addr is an + address + """ + client, addr = self._socket.accept() + conn = Connection(self._context, client) + conn.set_accept_state() + return (conn, addr) + + + def bio_shutdown(self): + """ + When using non-socket connections this function signals end of + data on the input for this connection. + + :return: None + """ + if self._from_ssl is None: + raise TypeError("Connection sock was not None") + + _lib.BIO_set_mem_eof_return(self._into_ssl, 0) + + + def shutdown(self): + """ + Send closure alert + + :return: True if the shutdown completed successfully (i.e. both sides + have sent closure alerts), false otherwise (i.e. you have to + wait for a ZeroReturnError on a recv() method call + """ + result = _lib.SSL_shutdown(self._ssl) + if result < 0: + # TODO: This is untested. + _raise_current_error() + elif result > 0: + return True + else: + return False + + + def get_cipher_list(self): + """ + Get the session cipher list + + :return: A list of cipher strings + """ + ciphers = [] + for i in count(): + result = _lib.SSL_get_cipher_list(self._ssl, i) + if result == _ffi.NULL: + break + ciphers.append(_native(_ffi.string(result))) + return ciphers + + + def get_client_ca_list(self): + """ + Get CAs whose certificates are suggested for client authentication. + + :return: If this is a server connection, a list of X509Names representing + the acceptable CAs as set by :py:meth:`OpenSSL.SSL.Context.set_client_ca_list` or + :py:meth:`OpenSSL.SSL.Context.add_client_ca`. If this is a client connection, + the list of such X509Names sent by the server, or an empty list if that + has not yet happened. + """ + ca_names = _lib.SSL_get_client_CA_list(self._ssl) + if ca_names == _ffi.NULL: + # TODO: This is untested. + return [] + + result = [] + for i in range(_lib.sk_X509_NAME_num(ca_names)): + name = _lib.sk_X509_NAME_value(ca_names, i) + copy = _lib.X509_NAME_dup(name) + if copy == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + pyname = X509Name.__new__(X509Name) + pyname._name = _ffi.gc(copy, _lib.X509_NAME_free) + result.append(pyname) + return result + + + def makefile(self): + """ + The makefile() method is not implemented, since there is no dup semantics + for SSL connections + + :raise NotImplementedError + """ + raise NotImplementedError("Cannot make file object of OpenSSL.SSL.Connection") + + + def get_app_data(self): + """ + Get application data + + :return: The application data + """ + return self._app_data + + + def set_app_data(self, data): + """ + Set application data + + :param data - The application data + :return: None + """ + self._app_data = data + + + def get_shutdown(self): + """ + Get shutdown state + + :return: The shutdown state, a bitvector of SENT_SHUTDOWN, RECEIVED_SHUTDOWN. + """ + return _lib.SSL_get_shutdown(self._ssl) + + + def set_shutdown(self, state): + """ + Set shutdown state + + :param state - bitvector of SENT_SHUTDOWN, RECEIVED_SHUTDOWN. + :return: None + """ + if not isinstance(state, integer_types): + raise TypeError("state must be an integer") + + _lib.SSL_set_shutdown(self._ssl, state) + + + def state_string(self): + """ + Get a verbose state description + + :return: A string representing the state + """ + + def server_random(self): + """ + Get a copy of the server hello nonce. + + :return: A string representing the state + """ + if self._ssl.session == _ffi.NULL: + return None + return _ffi.buffer( + self._ssl.s3.server_random, + _lib.SSL3_RANDOM_SIZE)[:] + + + def client_random(self): + """ + Get a copy of the client hello nonce. + + :return: A string representing the state + """ + if self._ssl.session == _ffi.NULL: + return None + return _ffi.buffer( + self._ssl.s3.client_random, + _lib.SSL3_RANDOM_SIZE)[:] + + + def master_key(self): + """ + Get a copy of the master key. + + :return: A string representing the state + """ + if self._ssl.session == _ffi.NULL: + return None + return _ffi.buffer( + self._ssl.session.master_key, + self._ssl.session.master_key_length)[:] + + + def sock_shutdown(self, *args, **kwargs): + """ + See shutdown(2) + + :return: What the socket's shutdown() method returns + """ + return self._socket.shutdown(*args, **kwargs) + + + def get_peer_certificate(self): + """ + Retrieve the other side's certificate (if any) + + :return: The peer's certificate + """ + cert = _lib.SSL_get_peer_certificate(self._ssl) + if cert != _ffi.NULL: + pycert = X509.__new__(X509) + pycert._x509 = _ffi.gc(cert, _lib.X509_free) + return pycert + return None + + + def get_peer_cert_chain(self): + """ + Retrieve the other side's certificate (if any) + + :return: A list of X509 instances giving the peer's certificate chain, + or None if it does not have one. + """ + cert_stack = _lib.SSL_get_peer_cert_chain(self._ssl) + if cert_stack == _ffi.NULL: + return None + + result = [] + for i in range(_lib.sk_X509_num(cert_stack)): + # TODO could incref instead of dup here + cert = _lib.X509_dup(_lib.sk_X509_value(cert_stack, i)) + pycert = X509.__new__(X509) + pycert._x509 = _ffi.gc(cert, _lib.X509_free) + result.append(pycert) + return result + + + def want_read(self): + """ + Checks if more data has to be read from the transport layer to complete an + operation. + + :return: True iff more data has to be read + """ + return _lib.SSL_want_read(self._ssl) + + + def want_write(self): + """ + Checks if there is data to write to the transport layer to complete an + operation. + + :return: True iff there is data to write + """ + return _lib.SSL_want_write(self._ssl) + + + def set_accept_state(self): + """ + Set the connection to work in server mode. The handshake will be handled + automatically by read/write. + + :return: None + """ + _lib.SSL_set_accept_state(self._ssl) + + + def set_connect_state(self): + """ + Set the connection to work in client mode. The handshake will be handled + automatically by read/write. + + :return: None + """ + _lib.SSL_set_connect_state(self._ssl) + + + def get_session(self): + """ + Returns the Session currently used. + + @return: An instance of :py:class:`OpenSSL.SSL.Session` or :py:obj:`None` if + no session exists. + """ + session = _lib.SSL_get1_session(self._ssl) + if session == _ffi.NULL: + return None + + pysession = Session.__new__(Session) + pysession._session = _ffi.gc(session, _lib.SSL_SESSION_free) + return pysession + + + def set_session(self, session): + """ + Set the session to be used when the TLS/SSL connection is established. + + :param session: A Session instance representing the session to use. + :returns: None + """ + if not isinstance(session, Session): + raise TypeError("session must be a Session instance") + + result = _lib.SSL_set_session(self._ssl, session._session) + if not result: + _raise_current_error() + +ConnectionType = Connection + +# This is similar to the initialization calls at the end of OpenSSL/crypto.py +# but is exercised mostly by the Context initializer. +_lib.SSL_library_init() diff --git a/OpenSSL/__init__.py b/OpenSSL/__init__.py index c9ea33b..db96e1f 100644 --- a/OpenSSL/__init__.py +++ b/OpenSSL/__init__.py @@ -5,40 +5,7 @@ pyOpenSSL - A simple wrapper around the OpenSSL library """ -import sys - -try: - orig = sys.getdlopenflags() -except AttributeError: - from OpenSSL import crypto -else: - try: - import DLFCN - except ImportError: - try: - import dl - except ImportError: - try: - import ctypes - except ImportError: - flags = 2 | 256 - else: - flags = 2 | ctypes.RTLD_GLOBAL - del ctypes - else: - flags = dl.RTLD_NOW | dl.RTLD_GLOBAL - del dl - else: - flags = DLFCN.RTLD_NOW | DLFCN.RTLD_GLOBAL - del DLFCN - - sys.setdlopenflags(flags) - from OpenSSL import crypto - sys.setdlopenflags(orig) - del orig, flags -del sys - -from OpenSSL import rand, SSL +from OpenSSL import rand, crypto, SSL from OpenSSL.version import __version__ __all__ = [ diff --git a/OpenSSL/_util.py b/OpenSSL/_util.py new file mode 100644 index 0000000..baeecc6 --- /dev/null +++ b/OpenSSL/_util.py @@ -0,0 +1,53 @@ +from six import PY3, binary_type, text_type + +from cryptography.hazmat.bindings.openssl.binding import Binding +binding = Binding() +ffi = binding.ffi +lib = binding.lib + +def exception_from_error_queue(exceptionType): + def text(charp): + return native(ffi.string(charp)) + + errors = [] + while True: + error = lib.ERR_get_error() + if error == 0: + break + errors.append(( + text(lib.ERR_lib_error_string(error)), + text(lib.ERR_func_error_string(error)), + text(lib.ERR_reason_error_string(error)))) + + raise exceptionType(errors) + + + +def native(s): + """ + Convert :py:class:`bytes` or :py:class:`unicode` to the native + :py:class:`str` type, using UTF-8 encoding if conversion is necessary. + + :raise UnicodeError: The input string is not UTF-8 decodeable. + + :raise TypeError: The input is neither :py:class:`bytes` nor + :py:class:`unicode`. + """ + if not isinstance(s, (binary_type, text_type)): + raise TypeError("%r is neither bytes nor unicode" % s) + if PY3: + if isinstance(s, binary_type): + return s.decode("utf-8") + else: + if isinstance(s, text_type): + return s.encode("utf-8") + return s + + + +if PY3: + def byte_string(s): + return s.encode("charmap") +else: + def byte_string(s): + return s diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py new file mode 100644 index 0000000..d0026bd --- /dev/null +++ b/OpenSSL/crypto.py @@ -0,0 +1,2314 @@ +from time import time +from base64 import b16encode +from functools import partial +from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__ + +from six import ( + integer_types as _integer_types, + text_type as _text_type) + +from OpenSSL._util import ( + ffi as _ffi, + lib as _lib, + exception_from_error_queue as _exception_from_error_queue, + byte_string as _byte_string, + native as _native) + +FILETYPE_PEM = _lib.SSL_FILETYPE_PEM +FILETYPE_ASN1 = _lib.SSL_FILETYPE_ASN1 + +# TODO This was an API mistake. OpenSSL has no such constant. +FILETYPE_TEXT = 2 ** 16 - 1 + +TYPE_RSA = _lib.EVP_PKEY_RSA +TYPE_DSA = _lib.EVP_PKEY_DSA + + +class Error(Exception): + """ + An error occurred in an `OpenSSL.crypto` API. + """ + + +_raise_current_error = partial(_exception_from_error_queue, Error) + +def _untested_error(where): + """ + An OpenSSL API failed somehow. Additionally, the failure which was + encountered isn't one that's exercised by the test suite so future behavior + of pyOpenSSL is now somewhat less predictable. + """ + raise RuntimeError("Unknown %s failure" % (where,)) + + + +def _new_mem_buf(buffer=None): + """ + Allocate a new OpenSSL memory BIO. + + Arrange for the garbage collector to clean it up automatically. + + :param buffer: None or some bytes to use to put into the BIO so that they + can be read out. + """ + if buffer is None: + bio = _lib.BIO_new(_lib.BIO_s_mem()) + free = _lib.BIO_free + else: + data = _ffi.new("char[]", buffer) + bio = _lib.BIO_new_mem_buf(data, len(buffer)) + # Keep the memory alive as long as the bio is alive! + def free(bio, ref=data): + return _lib.BIO_free(bio) + + if bio == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + bio = _ffi.gc(bio, free) + return bio + + + +def _bio_to_string(bio): + """ + Copy the contents of an OpenSSL BIO object into a Python byte string. + """ + result_buffer = _ffi.new('char**') + buffer_length = _lib.BIO_get_mem_data(bio, result_buffer) + return _ffi.buffer(result_buffer[0], buffer_length)[:] + + + +def _set_asn1_time(boundary, when): + """ + The the time value of an ASN1 time object. + + @param boundary: An ASN1_GENERALIZEDTIME pointer (or an object safely + castable to that type) which will have its value set. + @param when: A string representation of the desired time value. + + @raise TypeError: If C{when} is not a L{bytes} string. + @raise ValueError: If C{when} does not represent a time in the required + format. + @raise RuntimeError: If the time value cannot be set for some other + (unspecified) reason. + """ + if not isinstance(when, bytes): + raise TypeError("when must be a byte string") + + set_result = _lib.ASN1_GENERALIZEDTIME_set_string( + _ffi.cast('ASN1_GENERALIZEDTIME*', boundary), when) + if set_result == 0: + dummy = _ffi.gc(_lib.ASN1_STRING_new(), _lib.ASN1_STRING_free) + _lib.ASN1_STRING_set(dummy, when, len(when)) + check_result = _lib.ASN1_GENERALIZEDTIME_check( + _ffi.cast('ASN1_GENERALIZEDTIME*', dummy)) + if not check_result: + raise ValueError("Invalid string") + else: + _untested_error() + + + +def _get_asn1_time(timestamp): + """ + Retrieve the time value of an ASN1 time object. + + @param timestamp: An ASN1_GENERALIZEDTIME* (or an object safely castable to + that type) from which the time value will be retrieved. + + @return: The time value from C{timestamp} as a L{bytes} string in a certain + format. Or C{None} if the object contains no time value. + """ + string_timestamp = _ffi.cast('ASN1_STRING*', timestamp) + if _lib.ASN1_STRING_length(string_timestamp) == 0: + return None + elif _lib.ASN1_STRING_type(string_timestamp) == _lib.V_ASN1_GENERALIZEDTIME: + return _ffi.string(_lib.ASN1_STRING_data(string_timestamp)) + else: + generalized_timestamp = _ffi.new("ASN1_GENERALIZEDTIME**") + _lib.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp) + if generalized_timestamp[0] == _ffi.NULL: + # This may happen: + # - if timestamp was not an ASN1_TIME + # - if allocating memory for the ASN1_GENERALIZEDTIME failed + # - if a copy of the time data from timestamp cannot be made for + # the newly allocated ASN1_GENERALIZEDTIME + # + # These are difficult to test. cffi enforces the ASN1_TIME type. + # Memory allocation failures are a pain to trigger + # deterministically. + _untested_error("ASN1_TIME_to_generalizedtime") + else: + string_timestamp = _ffi.cast( + "ASN1_STRING*", generalized_timestamp[0]) + string_data = _lib.ASN1_STRING_data(string_timestamp) + string_result = _ffi.string(string_data) + _lib.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0]) + return string_result + + + +class PKey(object): + _only_public = False + _initialized = True + + def __init__(self): + pkey = _lib.EVP_PKEY_new() + self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free) + self._initialized = False + + + def generate_key(self, type, bits): + """ + Generate a key of a given type, with a given number of a bits + + :param type: The key type (TYPE_RSA or TYPE_DSA) + :param bits: The number of bits + + :return: None + """ + if not isinstance(type, int): + raise TypeError("type must be an integer") + + if not isinstance(bits, int): + raise TypeError("bits must be an integer") + + # TODO Check error return + exponent = _lib.BN_new() + exponent = _ffi.gc(exponent, _lib.BN_free) + _lib.BN_set_word(exponent, _lib.RSA_F4) + + if type == TYPE_RSA: + if bits <= 0: + raise ValueError("Invalid number of bits") + + rsa = _lib.RSA_new() + + result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL) + if result == 0: + # TODO: The test for this case is commented out. Different + # builds of OpenSSL appear to have different failure modes that + # make it hard to test. Visual inspection of the OpenSSL + # source reveals that a return value of 0 signals an error. + # Manual testing on a particular build of OpenSSL suggests that + # this is probably the appropriate way to handle those errors. + _raise_current_error() + + result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa) + if not result: + # TODO: It appears as though this can fail if an engine is in + # use which does not support RSA. + _raise_current_error() + + elif type == TYPE_DSA: + dsa = _lib.DSA_generate_parameters( + bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL) + if dsa == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + if not _lib.DSA_generate_key(dsa): + # TODO: This is untested. + _raise_current_error() + if not _lib.EVP_PKEY_assign_DSA(self._pkey, dsa): + # TODO: This is untested. + _raise_current_error() + else: + raise Error("No such key type") + + self._initialized = True + + + def check(self): + """ + Check the consistency of an RSA private key. + + :return: True if key is consistent. + :raise Error: if the key is inconsistent. + :raise TypeError: if the key is of a type which cannot be checked. + Only RSA keys can currently be checked. + """ + if self._only_public: + raise TypeError("public key only") + + if _lib.EVP_PKEY_type(self._pkey.type) != _lib.EVP_PKEY_RSA: + raise TypeError("key type unsupported") + + rsa = _lib.EVP_PKEY_get1_RSA(self._pkey) + rsa = _ffi.gc(rsa, _lib.RSA_free) + result = _lib.RSA_check_key(rsa) + if result: + return True + _raise_current_error() + + + def type(self): + """ + Returns the type of the key + + :return: The type of the key. + """ + return self._pkey.type + + + def bits(self): + """ + Returns the number of bits of the key + + :return: The number of bits of the key. + """ + return _lib.EVP_PKEY_bits(self._pkey) +PKeyType = PKey + + + +class X509Name(object): + def __init__(self, name): + """ + Create a new X509Name, copying the given X509Name instance. + + :param name: An X509Name object to copy + """ + name = _lib.X509_NAME_dup(name._name) + self._name = _ffi.gc(name, _lib.X509_NAME_free) + + + def __setattr__(self, name, value): + if name.startswith('_'): + return super(X509Name, self).__setattr__(name, value) + + # Note: we really do not want str subclasses here, so we do not use + # isinstance. + if type(name) is not str: + raise TypeError("attribute name must be string, not '%.200s'" % ( + type(value).__name__,)) + + nid = _lib.OBJ_txt2nid(_byte_string(name)) + if nid == _lib.NID_undef: + try: + _raise_current_error() + except Error: + pass + raise AttributeError("No such attribute") + + # If there's an old entry for this NID, remove it + for i in range(_lib.X509_NAME_entry_count(self._name)): + ent = _lib.X509_NAME_get_entry(self._name, i) + ent_obj = _lib.X509_NAME_ENTRY_get_object(ent) + ent_nid = _lib.OBJ_obj2nid(ent_obj) + if nid == ent_nid: + ent = _lib.X509_NAME_delete_entry(self._name, i) + _lib.X509_NAME_ENTRY_free(ent) + break + + if isinstance(value, _text_type): + value = value.encode('utf-8') + + add_result = _lib.X509_NAME_add_entry_by_NID( + self._name, nid, _lib.MBSTRING_UTF8, value, -1, -1, 0) + if not add_result: + _raise_current_error() + + + def __getattr__(self, name): + """ + Find attribute. An X509Name object has the following attributes: + countryName (alias C), stateOrProvince (alias ST), locality (alias L), + organization (alias O), organizationalUnit (alias OU), commonName (alias + CN) and more... + """ + nid = _lib.OBJ_txt2nid(_byte_string(name)) + if nid == _lib.NID_undef: + # This is a bit weird. OBJ_txt2nid indicated failure, but it seems + # a lower level function, a2d_ASN1_OBJECT, also feels the need to + # push something onto the error queue. If we don't clean that up + # now, someone else will bump into it later and be quite confused. + # See lp#314814. + try: + _raise_current_error() + except Error: + pass + return super(X509Name, self).__getattr__(name) + + entry_index = _lib.X509_NAME_get_index_by_NID(self._name, nid, -1) + if entry_index == -1: + return None + + entry = _lib.X509_NAME_get_entry(self._name, entry_index) + data = _lib.X509_NAME_ENTRY_get_data(entry) + + result_buffer = _ffi.new("unsigned char**") + data_length = _lib.ASN1_STRING_to_UTF8(result_buffer, data) + if data_length < 0: + # TODO: This is untested. + _raise_current_error() + + try: + result = _ffi.buffer(result_buffer[0], data_length)[:].decode('utf-8') + finally: + # XXX untested + _lib.OPENSSL_free(result_buffer[0]) + return result + + + def _cmp(op): + def f(self, other): + if not isinstance(other, X509Name): + return NotImplemented + result = _lib.X509_NAME_cmp(self._name, other._name) + return op(result, 0) + return f + + __eq__ = _cmp(__eq__) + __ne__ = _cmp(__ne__) + + __lt__ = _cmp(__lt__) + __le__ = _cmp(__le__) + + __gt__ = _cmp(__gt__) + __ge__ = _cmp(__ge__) + + def __repr__(self): + """ + String representation of an X509Name + """ + result_buffer = _ffi.new("char[]", 512); + format_result = _lib.X509_NAME_oneline( + self._name, result_buffer, len(result_buffer)) + + if format_result == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + return "" % ( + _native(_ffi.string(result_buffer)),) + + + def hash(self): + """ + Return the hash value of this name + + :return: None + """ + return _lib.X509_NAME_hash(self._name) + + + def der(self): + """ + Return the DER encoding of this name + + :return: A :py:class:`bytes` instance giving the DER encoded form of + this name. + """ + result_buffer = _ffi.new('unsigned char**') + encode_result = _lib.i2d_X509_NAME(self._name, result_buffer) + if encode_result < 0: + # TODO: This is untested. + _raise_current_error() + + string_result = _ffi.buffer(result_buffer[0], encode_result)[:] + _lib.OPENSSL_free(result_buffer[0]) + return string_result + + + def get_components(self): + """ + Returns the split-up components of this name. + + :return: List of tuples (name, value). + """ + result = [] + for i in range(_lib.X509_NAME_entry_count(self._name)): + ent = _lib.X509_NAME_get_entry(self._name, i) + + fname = _lib.X509_NAME_ENTRY_get_object(ent) + fval = _lib.X509_NAME_ENTRY_get_data(ent) + + nid = _lib.OBJ_obj2nid(fname) + name = _lib.OBJ_nid2sn(nid) + + result.append(( + _ffi.string(name), + _ffi.string( + _lib.ASN1_STRING_data(fval), + _lib.ASN1_STRING_length(fval)))) + + return result +X509NameType = X509Name + + +class X509Extension(object): + def __init__(self, type_name, critical, value, subject=None, issuer=None): + """ + :param typename: The name of the extension to create. + :type typename: :py:data:`str` + + :param critical: A flag indicating whether this is a critical extension. + + :param value: The value of the extension. + :type value: :py:data:`str` + + :param subject: Optional X509 cert to use as subject. + :type subject: :py:class:`X509` + + :param issuer: Optional X509 cert to use as issuer. + :type issuer: :py:class:`X509` + + :return: The X509Extension object + """ + ctx = _ffi.new("X509V3_CTX*") + + # A context is necessary for any extension which uses the r2i conversion + # method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx. + # Start off by initializing most of the fields to NULL. + _lib.X509V3_set_ctx(ctx, _ffi.NULL, _ffi.NULL, _ffi.NULL, _ffi.NULL, 0) + + # We have no configuration database - but perhaps we should (some + # extensions may require it). + _lib.X509V3_set_ctx_nodb(ctx) + + # Initialize the subject and issuer, if appropriate. ctx is a local, + # and as far as I can tell none of the X509V3_* APIs invoked here steal + # any references, so no need to mess with reference counts or duplicates. + if issuer is not None: + if not isinstance(issuer, X509): + raise TypeError("issuer must be an X509 instance") + ctx.issuer_cert = issuer._x509 + if subject is not None: + if not isinstance(subject, X509): + raise TypeError("subject must be an X509 instance") + ctx.subject_cert = subject._x509 + + if critical: + # There are other OpenSSL APIs which would let us pass in critical + # separately, but they're harder to use, and since value is already + # a pile of crappy junk smuggling a ton of utterly important + # structured data, what's the point of trying to avoid nasty stuff + # with strings? (However, X509V3_EXT_i2d in particular seems like it + # would be a better API to invoke. I do not know where to get the + # ext_struc it desires for its last parameter, though.) + value = b"critical," + value + + extension = _lib.X509V3_EXT_nconf(_ffi.NULL, ctx, type_name, value) + if extension == _ffi.NULL: + _raise_current_error() + self._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free) + + + @property + def _nid(self): + return _lib.OBJ_obj2nid(self._extension.object) + + _prefixes = { + _lib.GEN_EMAIL: "email", + _lib.GEN_DNS: "DNS", + _lib.GEN_URI: "URI", + } + + def _subjectAltNameString(self): + method = _lib.X509V3_EXT_get(self._extension) + if method == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + payload = self._extension.value.data + length = self._extension.value.length + + payloadptr = _ffi.new("unsigned char**") + payloadptr[0] = payload + + if method.it != _ffi.NULL: + ptr = _lib.ASN1_ITEM_ptr(method.it) + data = _lib.ASN1_item_d2i(_ffi.NULL, payloadptr, length, ptr) + names = _ffi.cast("GENERAL_NAMES*", data) + else: + names = _ffi.cast( + "GENERAL_NAMES*", + method.d2i(_ffi.NULL, payloadptr, length)) + + parts = [] + for i in range(_lib.sk_GENERAL_NAME_num(names)): + name = _lib.sk_GENERAL_NAME_value(names, i) + try: + label = self._prefixes[name.type] + except KeyError: + bio = _new_mem_buf() + _lib.GENERAL_NAME_print(bio, name) + parts.append(_native(_bio_to_string(bio))) + else: + value = _native( + _ffi.buffer(name.d.ia5.data, name.d.ia5.length)[:]) + parts.append(label + ":" + value) + return ", ".join(parts) + + + def __str__(self): + """ + :return: a nice text representation of the extension + """ + if _lib.NID_subject_alt_name == self._nid: + return self._subjectAltNameString() + + bio = _new_mem_buf() + print_result = _lib.X509V3_EXT_print(bio, self._extension, 0, 0) + if not print_result: + # TODO: This is untested. + _raise_current_error() + + return _native(_bio_to_string(bio)) + + + def get_critical(self): + """ + Returns the critical field of the X509Extension + + :return: The critical field. + """ + return _lib.X509_EXTENSION_get_critical(self._extension) + + + def get_short_name(self): + """ + Returns the short version of the type name of the X509Extension + + :return: The short type name. + """ + obj = _lib.X509_EXTENSION_get_object(self._extension) + nid = _lib.OBJ_obj2nid(obj) + return _ffi.string(_lib.OBJ_nid2sn(nid)) + + + def get_data(self): + """ + Returns the data of the X509Extension + + :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data. + """ + octet_result = _lib.X509_EXTENSION_get_data(self._extension) + string_result = _ffi.cast('ASN1_STRING*', octet_result) + char_result = _lib.ASN1_STRING_data(string_result) + result_length = _lib.ASN1_STRING_length(string_result) + return _ffi.buffer(char_result, result_length)[:] + +X509ExtensionType = X509Extension + + +class X509Req(object): + def __init__(self): + req = _lib.X509_REQ_new() + self._req = _ffi.gc(req, _lib.X509_REQ_free) + + + def set_pubkey(self, pkey): + """ + Set the public key of the certificate request + + :param pkey: The public key to use + :return: None + """ + set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey) + if not set_result: + # TODO: This is untested. + _raise_current_error() + + + def get_pubkey(self): + """ + Get the public key from the certificate request + + :return: The public key + """ + pkey = PKey.__new__(PKey) + pkey._pkey = _lib.X509_REQ_get_pubkey(self._req) + if pkey._pkey == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free) + pkey._only_public = True + return pkey + + + def set_version(self, version): + """ + Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate + request. + + :param version: The version number + :return: None + """ + set_result = _lib.X509_REQ_set_version(self._req, version) + if not set_result: + _raise_current_error() + + + def get_version(self): + """ + Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate + request. + + :return: an integer giving the value of the version subfield + """ + return _lib.X509_REQ_get_version(self._req) + + + def get_subject(self): + """ + Create an X509Name object for the subject of the certificate request + + :return: An X509Name object + """ + name = X509Name.__new__(X509Name) + name._name = _lib.X509_REQ_get_subject_name(self._req) + if name._name == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + # The name is owned by the X509Req structure. As long as the X509Name + # Python object is alive, keep the X509Req Python object alive. + name._owner = self + + return name + + + def add_extensions(self, extensions): + """ + Add extensions to the request. + + :param extensions: a sequence of X509Extension objects + :return: None + """ + stack = _lib.sk_X509_EXTENSION_new_null() + if stack == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + stack = _ffi.gc(stack, _lib.sk_X509_EXTENSION_free) + + for ext in extensions: + if not isinstance(ext, X509Extension): + raise ValueError("One of the elements is not an X509Extension") + + # TODO push can fail (here and elsewhere) + _lib.sk_X509_EXTENSION_push(stack, ext._extension) + + add_result = _lib.X509_REQ_add_extensions(self._req, stack) + if not add_result: + # TODO: This is untested. + _raise_current_error() + + + def sign(self, pkey, digest): + """ + Sign the certificate request using the supplied key and digest + + :param pkey: The key to sign with + :param digest: The message digest to use + :return: None + """ + if pkey._only_public: + raise ValueError("Key has only public part") + + if not pkey._initialized: + raise ValueError("Key is uninitialized") + + digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) + if digest_obj == _ffi.NULL: + raise ValueError("No such digest method") + + sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj) + if not sign_result: + # TODO: This is untested. + _raise_current_error() + + + def verify(self, pkey): + """ + Verifies a certificate request using the supplied public key + + :param key: a public key + :return: True if the signature is correct. + + :raise OpenSSL.crypto.Error: If the signature is invalid or there is a + problem verifying the signature. + """ + if not isinstance(pkey, PKey): + raise TypeError("pkey must be a PKey instance") + + result = _lib.X509_REQ_verify(self._req, pkey._pkey) + if result <= 0: + _raise_current_error() + + return result + + +X509ReqType = X509Req + + + +class X509(object): + def __init__(self): + # TODO Allocation failure? And why not __new__ instead of __init__? + x509 = _lib.X509_new() + self._x509 = _ffi.gc(x509, _lib.X509_free) + + + def set_version(self, version): + """ + Set version number of the certificate + + :param version: The version number + :type version: :py:class:`int` + + :return: None + """ + if not isinstance(version, int): + raise TypeError("version must be an integer") + + _lib.X509_set_version(self._x509, version) + + + def get_version(self): + """ + Return version number of the certificate + + :return: Version number as a Python integer + """ + return _lib.X509_get_version(self._x509) + + + def get_pubkey(self): + """ + Get the public key of the certificate + + :return: The public key + """ + pkey = PKey.__new__(PKey) + pkey._pkey = _lib.X509_get_pubkey(self._x509) + if pkey._pkey == _ffi.NULL: + _raise_current_error() + pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free) + pkey._only_public = True + return pkey + + + def set_pubkey(self, pkey): + """ + Set the public key of the certificate + + :param pkey: The public key + + :return: None + """ + if not isinstance(pkey, PKey): + raise TypeError("pkey must be a PKey instance") + + set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey) + if not set_result: + _raise_current_error() + + + def sign(self, pkey, digest): + """ + Sign the certificate using the supplied key and digest + + :param pkey: The key to sign with + :param digest: The message digest to use + :return: None + """ + if not isinstance(pkey, PKey): + raise TypeError("pkey must be a PKey instance") + + if pkey._only_public: + raise ValueError("Key only has public part") + + if not pkey._initialized: + raise ValueError("Key is uninitialized") + + evp_md = _lib.EVP_get_digestbyname(_byte_string(digest)) + if evp_md == _ffi.NULL: + raise ValueError("No such digest method") + + sign_result = _lib.X509_sign(self._x509, pkey._pkey, evp_md) + if not sign_result: + _raise_current_error() + + + def get_signature_algorithm(self): + """ + Retrieve the signature algorithm used in the certificate + + :return: A byte string giving the name of the signature algorithm used in + the certificate. + :raise ValueError: If the signature algorithm is undefined. + """ + alg = self._x509.cert_info.signature.algorithm + nid = _lib.OBJ_obj2nid(alg) + if nid == _lib.NID_undef: + raise ValueError("Undefined signature algorithm") + return _ffi.string(_lib.OBJ_nid2ln(nid)) + + + def digest(self, digest_name): + """ + Return the digest of the X509 object. + + :param digest_name: The name of the digest algorithm to use. + :type digest_name: :py:class:`bytes` + + :return: The digest of the object + """ + digest = _lib.EVP_get_digestbyname(_byte_string(digest_name)) + if digest == _ffi.NULL: + raise ValueError("No such digest method") + + result_buffer = _ffi.new("char[]", _lib.EVP_MAX_MD_SIZE) + result_length = _ffi.new("unsigned int[]", 1) + result_length[0] = len(result_buffer) + + digest_result = _lib.X509_digest( + self._x509, digest, result_buffer, result_length) + + if not digest_result: + # TODO: This is untested. + _raise_current_error() + + return b":".join([ + b16encode(ch).upper() for ch + in _ffi.buffer(result_buffer, result_length[0])]) + + + def subject_name_hash(self): + """ + Return the hash of the X509 subject. + + :return: The hash of the subject. + """ + return _lib.X509_subject_name_hash(self._x509) + + + def set_serial_number(self, serial): + """ + Set serial number of the certificate + + :param serial: The serial number + :type serial: :py:class:`int` + + :return: None + """ + if not isinstance(serial, _integer_types): + raise TypeError("serial must be an integer") + + hex_serial = hex(serial)[2:] + if not isinstance(hex_serial, bytes): + hex_serial = hex_serial.encode('ascii') + + bignum_serial = _ffi.new("BIGNUM**") + + # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like + # it. If bignum is still NULL after this call, then the return value is + # actually the result. I hope. -exarkun + small_serial = _lib.BN_hex2bn(bignum_serial, hex_serial) + + if bignum_serial[0] == _ffi.NULL: + set_result = _lib.ASN1_INTEGER_set( + _lib.X509_get_serialNumber(self._x509), small_serial) + if set_result: + # TODO Not tested + _raise_current_error() + else: + asn1_serial = _lib.BN_to_ASN1_INTEGER(bignum_serial[0], _ffi.NULL) + _lib.BN_free(bignum_serial[0]) + if asn1_serial == _ffi.NULL: + # TODO Not tested + _raise_current_error() + asn1_serial = _ffi.gc(asn1_serial, _lib.ASN1_INTEGER_free) + set_result = _lib.X509_set_serialNumber(self._x509, asn1_serial) + if not set_result: + # TODO Not tested + _raise_current_error() + + + def get_serial_number(self): + """ + Return serial number of the certificate + + :return: Serial number as a Python integer + """ + asn1_serial = _lib.X509_get_serialNumber(self._x509) + bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL) + try: + hex_serial = _lib.BN_bn2hex(bignum_serial) + try: + hexstring_serial = _ffi.string(hex_serial) + serial = int(hexstring_serial, 16) + return serial + finally: + _lib.OPENSSL_free(hex_serial) + finally: + _lib.BN_free(bignum_serial) + + + def gmtime_adj_notAfter(self, amount): + """ + Adjust the time stamp for when the certificate stops being valid + + :param amount: The number of seconds by which to adjust the ending + validity time. + :type amount: :py:class:`int` + + :return: None + """ + if not isinstance(amount, int): + raise TypeError("amount must be an integer") + + notAfter = _lib.X509_get_notAfter(self._x509) + _lib.X509_gmtime_adj(notAfter, amount) + + + def gmtime_adj_notBefore(self, amount): + """ + Change the timestamp for when the certificate starts being valid to the current + time plus an offset. + + :param amount: The number of seconds by which to adjust the starting validity + time. + :return: None + """ + if not isinstance(amount, int): + raise TypeError("amount must be an integer") + + notBefore = _lib.X509_get_notBefore(self._x509) + _lib.X509_gmtime_adj(notBefore, amount) + + + def has_expired(self): + """ + Check whether the certificate has expired. + + :return: True if the certificate has expired, false otherwise + """ + now = int(time()) + notAfter = _lib.X509_get_notAfter(self._x509) + return _lib.ASN1_UTCTIME_cmp_time_t( + _ffi.cast('ASN1_UTCTIME*', notAfter), now) < 0 + + + def _get_boundary_time(self, which): + return _get_asn1_time(which(self._x509)) + + + def get_notBefore(self): + """ + Retrieve the time stamp for when the certificate starts being valid + + :return: A string giving the timestamp, in the format:: + + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm + + or None if there is no value set. + """ + return self._get_boundary_time(_lib.X509_get_notBefore) + + + def _set_boundary_time(self, which, when): + return _set_asn1_time(which(self._x509), when) + + + def set_notBefore(self, when): + """ + Set the time stamp for when the certificate starts being valid + + :param when: A string giving the timestamp, in the format: + + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm + :type when: :py:class:`bytes` + + :return: None + """ + return self._set_boundary_time(_lib.X509_get_notBefore, when) + + + def get_notAfter(self): + """ + Retrieve the time stamp for when the certificate stops being valid + + :return: A string giving the timestamp, in the format:: + + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm + + or None if there is no value set. + """ + return self._get_boundary_time(_lib.X509_get_notAfter) + + + def set_notAfter(self, when): + """ + Set the time stamp for when the certificate stops being valid + + :param when: A string giving the timestamp, in the format: + + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm + :type when: :py:class:`bytes` + + :return: None + """ + return self._set_boundary_time(_lib.X509_get_notAfter, when) + + + def _get_name(self, which): + name = X509Name.__new__(X509Name) + name._name = which(self._x509) + if name._name == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + # The name is owned by the X509 structure. As long as the X509Name + # Python object is alive, keep the X509 Python object alive. + name._owner = self + + return name + + + def _set_name(self, which, name): + if not isinstance(name, X509Name): + raise TypeError("name must be an X509Name") + set_result = which(self._x509, name._name) + if not set_result: + # TODO: This is untested. + _raise_current_error() + + + def get_issuer(self): + """ + Create an X509Name object for the issuer of the certificate + + :return: An X509Name object + """ + return self._get_name(_lib.X509_get_issuer_name) + + + def set_issuer(self, issuer): + """ + Set the issuer of the certificate + + :param issuer: The issuer name + :type issuer: :py:class:`X509Name` + + :return: None + """ + return self._set_name(_lib.X509_set_issuer_name, issuer) + + + def get_subject(self): + """ + Create an X509Name object for the subject of the certificate + + :return: An X509Name object + """ + return self._get_name(_lib.X509_get_subject_name) + + + def set_subject(self, subject): + """ + Set the subject of the certificate + + :param subject: The subject name + :type subject: :py:class:`X509Name` + :return: None + """ + return self._set_name(_lib.X509_set_subject_name, subject) + + + def get_extension_count(self): + """ + Get the number of extensions on the certificate. + + :return: The number of extensions as an integer. + """ + return _lib.X509_get_ext_count(self._x509) + + + def add_extensions(self, extensions): + """ + Add extensions to the certificate. + + :param extensions: a sequence of X509Extension objects + :return: None + """ + for ext in extensions: + if not isinstance(ext, X509Extension): + raise ValueError("One of the elements is not an X509Extension") + + add_result = _lib.X509_add_ext(self._x509, ext._extension, -1) + if not add_result: + _raise_current_error() + + + def get_extension(self, index): + """ + Get a specific extension of the certificate by index. + + :param index: The index of the extension to retrieve. + :return: The X509Extension object at the specified index. + """ + ext = X509Extension.__new__(X509Extension) + ext._extension = _lib.X509_get_ext(self._x509, index) + if ext._extension == _ffi.NULL: + raise IndexError("extension index out of bounds") + + extension = _lib.X509_EXTENSION_dup(ext._extension) + ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free) + return ext + +X509Type = X509 + + + +class X509Store(object): + def __init__(self): + store = _lib.X509_STORE_new() + self._store = _ffi.gc(store, _lib.X509_STORE_free) + + + def add_cert(self, cert): + if not isinstance(cert, X509): + raise TypeError() + + result = _lib.X509_STORE_add_cert(self._store, cert._x509) + if not result: + _raise_current_error() + + +X509StoreType = X509Store + + + +def load_certificate(type, buffer): + """ + Load a certificate from a buffer + + :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1) + + :param buffer: The buffer the certificate is stored in + :type buffer: :py:class:`bytes` + + :return: The X509 object + """ + if isinstance(buffer, _text_type): + buffer = buffer.encode("ascii") + + bio = _new_mem_buf(buffer) + + if type == FILETYPE_PEM: + x509 = _lib.PEM_read_bio_X509(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL) + elif type == FILETYPE_ASN1: + x509 = _lib.d2i_X509_bio(bio, _ffi.NULL); + else: + raise ValueError( + "type argument must be FILETYPE_PEM or FILETYPE_ASN1") + + if x509 == _ffi.NULL: + _raise_current_error() + + cert = X509.__new__(X509) + cert._x509 = _ffi.gc(x509, _lib.X509_free) + return cert + + +def dump_certificate(type, cert): + """ + Dump a certificate to a buffer + + :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or + FILETYPE_TEXT) + :param cert: The certificate to dump + :return: The buffer with the dumped certificate in + """ + bio = _new_mem_buf() + + if type == FILETYPE_PEM: + result_code = _lib.PEM_write_bio_X509(bio, cert._x509) + elif type == FILETYPE_ASN1: + result_code = _lib.i2d_X509_bio(bio, cert._x509) + elif type == FILETYPE_TEXT: + result_code = _lib.X509_print_ex(bio, cert._x509, 0, 0) + else: + raise ValueError( + "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or " + "FILETYPE_TEXT") + + return _bio_to_string(bio) + + + +def dump_privatekey(type, pkey, cipher=None, passphrase=None): + """ + Dump a private key to a buffer + + :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1, or + FILETYPE_TEXT) + :param pkey: The PKey to dump + :param cipher: (optional) if encrypted PEM format, the cipher to + use + :param passphrase: (optional) if encrypted PEM format, this can be either + the passphrase to use, or a callback for providing the + passphrase. + :return: The buffer with the dumped key in + :rtype: :py:data:`str` + """ + bio = _new_mem_buf() + + if cipher is not None: + if passphrase is None: + raise TypeError( + "if a value is given for cipher " + "one must also be given for passphrase") + cipher_obj = _lib.EVP_get_cipherbyname(_byte_string(cipher)) + if cipher_obj == _ffi.NULL: + raise ValueError("Invalid cipher name") + else: + cipher_obj = _ffi.NULL + + helper = _PassphraseHelper(type, passphrase) + if type == FILETYPE_PEM: + result_code = _lib.PEM_write_bio_PrivateKey( + bio, pkey._pkey, cipher_obj, _ffi.NULL, 0, + helper.callback, helper.callback_args) + helper.raise_if_problem() + elif type == FILETYPE_ASN1: + result_code = _lib.i2d_PrivateKey_bio(bio, pkey._pkey) + elif type == FILETYPE_TEXT: + rsa = _lib.EVP_PKEY_get1_RSA(pkey._pkey) + result_code = _lib.RSA_print(bio, rsa, 0) + # TODO RSA_free(rsa)? + else: + raise ValueError( + "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or " + "FILETYPE_TEXT") + + if result_code == 0: + _raise_current_error() + + return _bio_to_string(bio) + + + +def _X509_REVOKED_dup(original): + copy = _lib.X509_REVOKED_new() + if copy == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + if original.serialNumber != _ffi.NULL: + copy.serialNumber = _lib.ASN1_INTEGER_dup(original.serialNumber) + + if original.revocationDate != _ffi.NULL: + copy.revocationDate = _lib.M_ASN1_TIME_dup(original.revocationDate) + + if original.extensions != _ffi.NULL: + extension_stack = _lib.sk_X509_EXTENSION_new_null() + for i in range(_lib.sk_X509_EXTENSION_num(original.extensions)): + original_ext = _lib.sk_X509_EXTENSION_value(original.extensions, i) + copy_ext = _lib.X509_EXTENSION_dup(original_ext) + _lib.sk_X509_EXTENSION_push(extension_stack, copy_ext) + copy.extensions = extension_stack + + copy.sequence = original.sequence + return copy + + + +class Revoked(object): + # http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_ + # which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches + # OCSP_crl_reason_str. We use the latter, just like the command line + # program. + _crl_reasons = [ + b"unspecified", + b"keyCompromise", + b"CACompromise", + b"affiliationChanged", + b"superseded", + b"cessationOfOperation", + b"certificateHold", + # b"removeFromCRL", + ] + + def __init__(self): + revoked = _lib.X509_REVOKED_new() + self._revoked = _ffi.gc(revoked, _lib.X509_REVOKED_free) + + + def set_serial(self, hex_str): + """ + Set the serial number of a revoked Revoked structure + + :param hex_str: The new serial number. + :type hex_str: :py:data:`str` + :return: None + """ + bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free) + bignum_ptr = _ffi.new("BIGNUM**") + bignum_ptr[0] = bignum_serial + bn_result = _lib.BN_hex2bn(bignum_ptr, hex_str) + if not bn_result: + raise ValueError("bad hex string") + + asn1_serial = _ffi.gc( + _lib.BN_to_ASN1_INTEGER(bignum_serial, _ffi.NULL), + _lib.ASN1_INTEGER_free) + _lib.X509_REVOKED_set_serialNumber(self._revoked, asn1_serial) + + + def get_serial(self): + """ + Return the serial number of a Revoked structure + + :return: The serial number as a string + """ + bio = _new_mem_buf() + + result = _lib.i2a_ASN1_INTEGER(bio, self._revoked.serialNumber) + if result < 0: + # TODO: This is untested. + _raise_current_error() + + return _bio_to_string(bio) + + + def _delete_reason(self): + stack = self._revoked.extensions + for i in range(_lib.sk_X509_EXTENSION_num(stack)): + ext = _lib.sk_X509_EXTENSION_value(stack, i) + if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason: + _lib.X509_EXTENSION_free(ext) + _lib.sk_X509_EXTENSION_delete(stack, i) + break + + + def set_reason(self, reason): + """ + Set the reason of a Revoked object. + + If :py:data:`reason` is :py:data:`None`, delete the reason instead. + + :param reason: The reason string. + :type reason: :py:class:`str` or :py:class:`NoneType` + :return: None + """ + if reason is None: + self._delete_reason() + elif not isinstance(reason, bytes): + raise TypeError("reason must be None or a byte string") + else: + reason = reason.lower().replace(b' ', b'') + reason_code = [r.lower() for r in self._crl_reasons].index(reason) + + new_reason_ext = _lib.ASN1_ENUMERATED_new() + if new_reason_ext == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free) + + set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code) + if set_result == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + self._delete_reason() + add_result = _lib.X509_REVOKED_add1_ext_i2d( + self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0) + + if not add_result: + # TODO: This is untested. + _raise_current_error() + + + def get_reason(self): + """ + Return the reason of a Revoked object. + + :return: The reason as a string + """ + extensions = self._revoked.extensions + for i in range(_lib.sk_X509_EXTENSION_num(extensions)): + ext = _lib.sk_X509_EXTENSION_value(extensions, i) + if _lib.OBJ_obj2nid(ext.object) == _lib.NID_crl_reason: + bio = _new_mem_buf() + + print_result = _lib.X509V3_EXT_print(bio, ext, 0, 0) + if not print_result: + print_result = _lib.M_ASN1_OCTET_STRING_print(bio, ext.value) + if print_result == 0: + # TODO: This is untested. + _raise_current_error() + + return _bio_to_string(bio) + + + def all_reasons(self): + """ + Return a list of all the supported reason strings. + + :return: A list of reason strings. + """ + return self._crl_reasons[:] + + + def set_rev_date(self, when): + """ + Set the revocation timestamp + + :param when: A string giving the timestamp, in the format: + + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm + + :return: None + """ + return _set_asn1_time(self._revoked.revocationDate, when) + + + def get_rev_date(self): + """ + Retrieve the revocation date + + :return: A string giving the timestamp, in the format: + + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm + """ + return _get_asn1_time(self._revoked.revocationDate) + + + +class CRL(object): + def __init__(self): + """ + Create a new empty CRL object. + """ + crl = _lib.X509_CRL_new() + self._crl = _ffi.gc(crl, _lib.X509_CRL_free) + + + def get_revoked(self): + """ + Return revoked portion of the CRL structure (by value not reference). + + :return: A tuple of Revoked objects. + """ + results = [] + revoked_stack = self._crl.crl.revoked + for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)): + revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i) + revoked_copy = _X509_REVOKED_dup(revoked) + pyrev = Revoked.__new__(Revoked) + pyrev._revoked = _ffi.gc(revoked_copy, _lib.X509_REVOKED_free) + results.append(pyrev) + if results: + return tuple(results) + + + def add_revoked(self, revoked): + """ + Add a revoked (by value not reference) to the CRL structure + + :param revoked: The new revoked. + :type revoked: :class:`X509` + + :return: None + """ + copy = _X509_REVOKED_dup(revoked._revoked) + if copy == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + add_result = _lib.X509_CRL_add0_revoked(self._crl, copy) + if add_result == 0: + # TODO: This is untested. + _raise_current_error() + + + def export(self, cert, key, type=FILETYPE_PEM, days=100): + """ + export a CRL as a string + + :param cert: Used to sign CRL. + :type cert: :class:`X509` + + :param key: Used to sign CRL. + :type key: :class:`PKey` + + :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`. + + :param days: The number of days until the next update of this CRL. + :type days: :py:data:`int` + + :return: :py:data:`str` + """ + if not isinstance(cert, X509): + raise TypeError("cert must be an X509 instance") + if not isinstance(key, PKey): + raise TypeError("key must be a PKey instance") + if not isinstance(type, int): + raise TypeError("type must be an integer") + + bio = _lib.BIO_new(_lib.BIO_s_mem()) + if bio == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + # A scratch time object to give different values to different CRL fields + sometime = _lib.ASN1_TIME_new() + if sometime == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + _lib.X509_gmtime_adj(sometime, 0) + _lib.X509_CRL_set_lastUpdate(self._crl, sometime) + + _lib.X509_gmtime_adj(sometime, days * 24 * 60 * 60) + _lib.X509_CRL_set_nextUpdate(self._crl, sometime) + + _lib.X509_CRL_set_issuer_name(self._crl, _lib.X509_get_subject_name(cert._x509)) + + sign_result = _lib.X509_CRL_sign(self._crl, key._pkey, _lib.EVP_md5()) + if not sign_result: + _raise_current_error() + + if type == FILETYPE_PEM: + ret = _lib.PEM_write_bio_X509_CRL(bio, self._crl) + elif type == FILETYPE_ASN1: + ret = _lib.i2d_X509_CRL_bio(bio, self._crl) + elif type == FILETYPE_TEXT: + ret = _lib.X509_CRL_print(bio, self._crl) + else: + raise ValueError( + "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT") + + if not ret: + # TODO: This is untested. + _raise_current_error() + + return _bio_to_string(bio) +CRLType = CRL + + + +class PKCS7(object): + def type_is_signed(self): + """ + Check if this NID_pkcs7_signed object + + :return: True if the PKCS7 is of type signed + """ + if _lib.PKCS7_type_is_signed(self._pkcs7): + return True + return False + + + def type_is_enveloped(self): + """ + Check if this NID_pkcs7_enveloped object + + :returns: True if the PKCS7 is of type enveloped + """ + if _lib.PKCS7_type_is_enveloped(self._pkcs7): + return True + return False + + + def type_is_signedAndEnveloped(self): + """ + Check if this NID_pkcs7_signedAndEnveloped object + + :returns: True if the PKCS7 is of type signedAndEnveloped + """ + if _lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7): + return True + return False + + + def type_is_data(self): + """ + Check if this NID_pkcs7_data object + + :return: True if the PKCS7 is of type data + """ + if _lib.PKCS7_type_is_data(self._pkcs7): + return True + return False + + + def get_type_name(self): + """ + Returns the type name of the PKCS7 structure + + :return: A string with the typename + """ + nid = _lib.OBJ_obj2nid(self._pkcs7.type) + string_type = _lib.OBJ_nid2sn(nid) + return _ffi.string(string_type) + +PKCS7Type = PKCS7 + + + +class PKCS12(object): + def __init__(self): + self._pkey = None + self._cert = None + self._cacerts = None + self._friendlyname = None + + + def get_certificate(self): + """ + Return certificate portion of the PKCS12 structure + + :return: X509 object containing the certificate + """ + return self._cert + + + def set_certificate(self, cert): + """ + Replace the certificate portion of the PKCS12 structure + + :param cert: The new certificate. + :type cert: :py:class:`X509` or :py:data:`None` + :return: None + """ + if not isinstance(cert, X509): + raise TypeError("cert must be an X509 instance") + self._cert = cert + + + def get_privatekey(self): + """ + Return private key portion of the PKCS12 structure + + :returns: PKey object containing the private key + """ + return self._pkey + + + def set_privatekey(self, pkey): + """ + Replace or set the certificate portion of the PKCS12 structure + + :param pkey: The new private key. + :type pkey: :py:class:`PKey` + :return: None + """ + if not isinstance(pkey, PKey): + raise TypeError("pkey must be a PKey instance") + self._pkey = pkey + + + def get_ca_certificates(self): + """ + Return CA certificates within of the PKCS12 object + + :return: A newly created tuple containing the CA certificates in the chain, + if any are present, or None if no CA certificates are present. + """ + if self._cacerts is not None: + return tuple(self._cacerts) + + + def set_ca_certificates(self, cacerts): + """ + Replace or set the CA certificates withing the PKCS12 object. + + :param cacerts: The new CA certificates. + :type cacerts: :py:data:`None` or an iterable of :py:class:`X509` + :return: None + """ + if cacerts is None: + self._cacerts = None + else: + cacerts = list(cacerts) + for cert in cacerts: + if not isinstance(cert, X509): + raise TypeError("iterable must only contain X509 instances") + self._cacerts = cacerts + + + def set_friendlyname(self, name): + """ + Replace or set the certificate portion of the PKCS12 structure + + :param name: The new friendly name. + :type name: :py:class:`bytes` + :return: None + """ + if name is None: + self._friendlyname = None + elif not isinstance(name, bytes): + raise TypeError("name must be a byte string or None (not %r)" % (name,)) + self._friendlyname = name + + + def get_friendlyname(self): + """ + Return friendly name portion of the PKCS12 structure + + :returns: String containing the friendlyname + """ + return self._friendlyname + + + def export(self, passphrase=None, iter=2048, maciter=1): + """ + Dump a PKCS12 object as a string. See also "man PKCS12_create". + + :param passphrase: used to encrypt the PKCS12 + :type passphrase: :py:data:`bytes` + + :param iter: How many times to repeat the encryption + :type iter: :py:data:`int` + + :param maciter: How many times to repeat the MAC + :type maciter: :py:data:`int` + + :return: The string containing the PKCS12 + """ + if self._cacerts is None: + cacerts = _ffi.NULL + else: + cacerts = _lib.sk_X509_new_null() + cacerts = _ffi.gc(cacerts, _lib.sk_X509_free) + for cert in self._cacerts: + _lib.sk_X509_push(cacerts, cert._x509) + + if passphrase is None: + passphrase = _ffi.NULL + + friendlyname = self._friendlyname + if friendlyname is None: + friendlyname = _ffi.NULL + + if self._pkey is None: + pkey = _ffi.NULL + else: + pkey = self._pkey._pkey + + if self._cert is None: + cert = _ffi.NULL + else: + cert = self._cert._x509 + + pkcs12 = _lib.PKCS12_create( + passphrase, friendlyname, pkey, cert, cacerts, + _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC, + _lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC, + iter, maciter, 0) + if pkcs12 == _ffi.NULL: + _raise_current_error() + pkcs12 = _ffi.gc(pkcs12, _lib.PKCS12_free) + + bio = _new_mem_buf() + _lib.i2d_PKCS12_bio(bio, pkcs12) + return _bio_to_string(bio) + +PKCS12Type = PKCS12 + + + +class NetscapeSPKI(object): + def __init__(self): + spki = _lib.NETSCAPE_SPKI_new() + self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free) + + + def sign(self, pkey, digest): + """ + Sign the certificate request using the supplied key and digest + + :param pkey: The key to sign with + :param digest: The message digest to use + :return: None + """ + if pkey._only_public: + raise ValueError("Key has only public part") + + if not pkey._initialized: + raise ValueError("Key is uninitialized") + + digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) + if digest_obj == _ffi.NULL: + raise ValueError("No such digest method") + + sign_result = _lib.NETSCAPE_SPKI_sign(self._spki, pkey._pkey, digest_obj) + if not sign_result: + # TODO: This is untested. + _raise_current_error() + + + def verify(self, key): + """ + Verifies a certificate request using the supplied public key + + :param key: a public key + :return: True if the signature is correct. + :raise OpenSSL.crypto.Error: If the signature is invalid or there is a + problem verifying the signature. + """ + answer = _lib.NETSCAPE_SPKI_verify(self._spki, key._pkey) + if answer <= 0: + _raise_current_error() + return True + + + def b64_encode(self): + """ + Generate a base64 encoded string from an SPKI + + :return: The base64 encoded string + """ + encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki) + result = _ffi.string(encoded) + _lib.CRYPTO_free(encoded) + return result + + + def get_pubkey(self): + """ + Get the public key of the certificate + + :return: The public key + """ + pkey = PKey.__new__(PKey) + pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki) + if pkey._pkey == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + pkey._pkey = _ffi.gc(pkey._pkey, _lib.EVP_PKEY_free) + pkey._only_public = True + return pkey + + + def set_pubkey(self, pkey): + """ + Set the public key of the certificate + + :param pkey: The public key + :return: None + """ + set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey) + if not set_result: + # TODO: This is untested. + _raise_current_error() +NetscapeSPKIType = NetscapeSPKI + + +class _PassphraseHelper(object): + def __init__(self, type, passphrase, more_args=False, truncate=False): + if type != FILETYPE_PEM and passphrase is not None: + raise ValueError("only FILETYPE_PEM key format supports encryption") + self._passphrase = passphrase + self._more_args = more_args + self._truncate = truncate + self._problems = [] + + + @property + def callback(self): + if self._passphrase is None: + return _ffi.NULL + elif isinstance(self._passphrase, bytes): + return _ffi.NULL + elif callable(self._passphrase): + return _ffi.callback("pem_password_cb", self._read_passphrase) + else: + raise TypeError("Last argument must be string or callable") + + + @property + def callback_args(self): + if self._passphrase is None: + return _ffi.NULL + elif isinstance(self._passphrase, bytes): + return self._passphrase + elif callable(self._passphrase): + return _ffi.NULL + else: + raise TypeError("Last argument must be string or callable") + + + def raise_if_problem(self, exceptionType=Error): + try: + _exception_from_error_queue(exceptionType) + except exceptionType as e: + from_queue = e + if self._problems: + raise self._problems[0] + return from_queue + + + def _read_passphrase(self, buf, size, rwflag, userdata): + try: + if self._more_args: + result = self._passphrase(size, rwflag, userdata) + else: + result = self._passphrase(rwflag) + if not isinstance(result, bytes): + raise ValueError("String expected") + if len(result) > size: + if self._truncate: + result = result[:size] + else: + raise ValueError("passphrase returned by callback is too long") + for i in range(len(result)): + buf[i] = result[i:i + 1] + return len(result) + except Exception as e: + self._problems.append(e) + return 0 + + + +def load_privatekey(type, buffer, passphrase=None): + """ + Load a private key from a buffer + + :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1) + :param buffer: The buffer the key is stored in + :param passphrase: (optional) if encrypted PEM format, this can be + either the passphrase to use, or a callback for + providing the passphrase. + + :return: The PKey object + """ + if isinstance(buffer, _text_type): + buffer = buffer.encode("ascii") + + bio = _new_mem_buf(buffer) + + helper = _PassphraseHelper(type, passphrase) + if type == FILETYPE_PEM: + evp_pkey = _lib.PEM_read_bio_PrivateKey( + bio, _ffi.NULL, helper.callback, helper.callback_args) + helper.raise_if_problem() + elif type == FILETYPE_ASN1: + evp_pkey = _lib.d2i_PrivateKey_bio(bio, _ffi.NULL) + else: + raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1") + + if evp_pkey == _ffi.NULL: + _raise_current_error() + + pkey = PKey.__new__(PKey) + pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free) + return pkey + + + +def dump_certificate_request(type, req): + """ + Dump a certificate request to a buffer + + :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1) + :param req: The certificate request to dump + :return: The buffer with the dumped certificate request in + """ + bio = _new_mem_buf() + + if type == FILETYPE_PEM: + result_code = _lib.PEM_write_bio_X509_REQ(bio, req._req) + elif type == FILETYPE_ASN1: + result_code = _lib.i2d_X509_REQ_bio(bio, req._req) + elif type == FILETYPE_TEXT: + result_code = _lib.X509_REQ_print_ex(bio, req._req, 0, 0) + else: + raise ValueError("type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT") + + if result_code == 0: + # TODO: This is untested. + _raise_current_error() + + return _bio_to_string(bio) + + + +def load_certificate_request(type, buffer): + """ + Load a certificate request from a buffer + + :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1) + :param buffer: The buffer the certificate request is stored in + :return: The X509Req object + """ + if isinstance(buffer, _text_type): + buffer = buffer.encode("ascii") + + bio = _new_mem_buf(buffer) + + if type == FILETYPE_PEM: + req = _lib.PEM_read_bio_X509_REQ(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL) + elif type == FILETYPE_ASN1: + req = _lib.d2i_X509_REQ_bio(bio, _ffi.NULL) + else: + raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1") + + if req == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + + x509req = X509Req.__new__(X509Req) + x509req._req = _ffi.gc(req, _lib.X509_REQ_free) + return x509req + + + +def sign(pkey, data, digest): + """ + Sign data with a digest + + :param pkey: Pkey to sign with + :param data: data to be signed + :param digest: message digest to use + :return: signature + """ + digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) + if digest_obj == _ffi.NULL: + raise ValueError("No such digest method") + + md_ctx = _ffi.new("EVP_MD_CTX*") + md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup) + + _lib.EVP_SignInit(md_ctx, digest_obj) + _lib.EVP_SignUpdate(md_ctx, data, len(data)) + + signature_buffer = _ffi.new("unsigned char[]", 512) + signature_length = _ffi.new("unsigned int*") + signature_length[0] = len(signature_buffer) + final_result = _lib.EVP_SignFinal( + md_ctx, signature_buffer, signature_length, pkey._pkey) + + if final_result != 1: + # TODO: This is untested. + _raise_current_error() + + return _ffi.buffer(signature_buffer, signature_length[0])[:] + + + +def verify(cert, signature, data, digest): + """ + Verify a signature + + :param cert: signing certificate (X509 object) + :param signature: signature returned by sign function + :param data: data to be verified + :param digest: message digest to use + :return: None if the signature is correct, raise exception otherwise + """ + digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest)) + if digest_obj == _ffi.NULL: + raise ValueError("No such digest method") + + pkey = _lib.X509_get_pubkey(cert._x509) + if pkey == _ffi.NULL: + # TODO: This is untested. + _raise_current_error() + pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free) + + md_ctx = _ffi.new("EVP_MD_CTX*") + md_ctx = _ffi.gc(md_ctx, _lib.EVP_MD_CTX_cleanup) + + _lib.EVP_VerifyInit(md_ctx, digest_obj) + _lib.EVP_VerifyUpdate(md_ctx, data, len(data)) + verify_result = _lib.EVP_VerifyFinal(md_ctx, signature, len(signature), pkey) + + if verify_result != 1: + _raise_current_error() + + + +def load_crl(type, buffer): + """ + Load a certificate revocation list from a buffer + + :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1) + :param buffer: The buffer the CRL is stored in + + :return: The PKey object + """ + if isinstance(buffer, _text_type): + buffer = buffer.encode("ascii") + + bio = _new_mem_buf(buffer) + + if type == FILETYPE_PEM: + crl = _lib.PEM_read_bio_X509_CRL(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL) + elif type == FILETYPE_ASN1: + crl = _lib.d2i_X509_CRL_bio(bio, _ffi.NULL) + else: + raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1") + + if crl == _ffi.NULL: + _raise_current_error() + + result = CRL.__new__(CRL) + result._crl = crl + return result + + + +def load_pkcs7_data(type, buffer): + """ + Load pkcs7 data from a buffer + + :param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1) + :param buffer: The buffer with the pkcs7 data. + :return: The PKCS7 object + """ + if isinstance(buffer, _text_type): + buffer = buffer.encode("ascii") + + bio = _new_mem_buf(buffer) + + if type == FILETYPE_PEM: + pkcs7 = _lib.PEM_read_bio_PKCS7(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL) + elif type == FILETYPE_ASN1: + pass + else: + # TODO: This is untested. + _raise_current_error() + raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1") + + if pkcs7 == _ffi.NULL: + _raise_current_error() + + pypkcs7 = PKCS7.__new__(PKCS7) + pypkcs7._pkcs7 = _ffi.gc(pkcs7, _lib.PKCS7_free) + return pypkcs7 + + + +def load_pkcs12(buffer, passphrase): + """ + Load a PKCS12 object from a buffer + + :param buffer: The buffer the certificate is stored in + :param passphrase: (Optional) The password to decrypt the PKCS12 lump + :returns: The PKCS12 object + """ + if isinstance(buffer, _text_type): + buffer = buffer.encode("ascii") + + bio = _new_mem_buf(buffer) + + p12 = _lib.d2i_PKCS12_bio(bio, _ffi.NULL) + if p12 == _ffi.NULL: + _raise_current_error() + p12 = _ffi.gc(p12, _lib.PKCS12_free) + + pkey = _ffi.new("EVP_PKEY**") + cert = _ffi.new("X509**") + cacerts = _ffi.new("Cryptography_STACK_OF_X509**") + + parse_result = _lib.PKCS12_parse(p12, passphrase, pkey, cert, cacerts) + if not parse_result: + _raise_current_error() + + cacerts = _ffi.gc(cacerts[0], _lib.sk_X509_free) + + # openssl 1.0.0 sometimes leaves an X509_check_private_key error in the + # queue for no particular reason. This error isn't interesting to anyone + # outside this function. It's not even interesting to us. Get rid of it. + try: + _raise_current_error() + except Error: + pass + + if pkey[0] == _ffi.NULL: + pykey = None + else: + pykey = PKey.__new__(PKey) + pykey._pkey = _ffi.gc(pkey[0], _lib.EVP_PKEY_free) + + if cert[0] == _ffi.NULL: + pycert = None + friendlyname = None + else: + pycert = X509.__new__(X509) + pycert._x509 = _ffi.gc(cert[0], _lib.X509_free) + + friendlyname_length = _ffi.new("int*") + friendlyname_buffer = _lib.X509_alias_get0(cert[0], friendlyname_length) + friendlyname = _ffi.buffer(friendlyname_buffer, friendlyname_length[0])[:] + if friendlyname_buffer == _ffi.NULL: + friendlyname = None + + pycacerts = [] + for i in range(_lib.sk_X509_num(cacerts)): + pycacert = X509.__new__(X509) + pycacert._x509 = _lib.sk_X509_value(cacerts, i) + pycacerts.append(pycacert) + if not pycacerts: + pycacerts = None + + pkcs12 = PKCS12.__new__(PKCS12) + pkcs12._pkey = pykey + pkcs12._cert = pycert + pkcs12._cacerts = pycacerts + pkcs12._friendlyname = friendlyname + return pkcs12 + + +def _initialize_openssl_threads(get_ident, Lock): + import _ssl + return + + locks = list(Lock() for n in range(_lib.CRYPTO_num_locks())) + + def locking_function(mode, index, filename, line): + if mode & _lib.CRYPTO_LOCK: + locks[index].acquire() + else: + locks[index].release() + + _lib.CRYPTO_set_id_callback( + _ffi.callback("unsigned long (*)(void)", get_ident)) + + _lib.CRYPTO_set_locking_callback( + _ffi.callback( + "void (*)(int, int, const char*, int)", locking_function)) + + +try: + from thread import get_ident + from threading import Lock +except ImportError: + pass +else: + _initialize_openssl_threads(get_ident, Lock) + del get_ident, Lock + +# There are no direct unit tests for this initialization. It is tested +# indirectly since it is necessary for functions like dump_privatekey when +# using encryption. +# +# Thus OpenSSL.test.test_crypto.FunctionTests.test_dump_privatekey_passphrase +# and some other similar tests may fail without this (though they may not if +# the Python runtime has already done some initialization of the underlying +# OpenSSL library (and is linked against the same one that cryptography is +# using)). +_lib.OpenSSL_add_all_algorithms() + +# This is similar but exercised mainly by exception_from_error_queue. It calls +# both ERR_load_crypto_strings() and ERR_load_SSL_strings(). +_lib.SSL_load_error_strings() diff --git a/OpenSSL/crypto/crl.c b/OpenSSL/crypto/crl.c deleted file mode 100644 index eec5bcb..0000000 --- a/OpenSSL/crypto/crl.c +++ /dev/null @@ -1,290 +0,0 @@ -#include -#define crypto_MODULE -#include "crypto.h" - - -static X509_REVOKED * X509_REVOKED_dup(X509_REVOKED *orig) { - X509_REVOKED *dupe = NULL; - - dupe = X509_REVOKED_new(); - if (dupe == NULL) { - return NULL; - } - if (orig->serialNumber) { - dupe->serialNumber = M_ASN1_INTEGER_dup(orig->serialNumber); - } - if (orig->revocationDate) { - dupe->revocationDate = M_ASN1_INTEGER_dup(orig->revocationDate); - } - if (orig->extensions) { - STACK_OF(X509_EXTENSION) *sk = NULL; - X509_EXTENSION * ext; - int j; - - sk = sk_X509_EXTENSION_new_null(); - for (j = 0; j < sk_X509_EXTENSION_num(orig->extensions); j++) { - ext = sk_X509_EXTENSION_value(orig->extensions, j); - ext = X509_EXTENSION_dup(ext); - sk_X509_EXTENSION_push(sk, ext); - } - dupe->extensions = sk; - } - dupe->sequence = orig->sequence; - return dupe; -} - -static char crypto_CRL_get_revoked_doc[] = "\n\ -Return revoked portion of the CRL structure (by value\n\ -not reference).\n\ -\n\ -@return: A tuple of Revoked objects.\n\ -"; -static PyObject * -crypto_CRL_get_revoked(crypto_CRLObj *self, PyObject *args) { - int j, num_rev; - X509_REVOKED *r = NULL; - PyObject *obj = NULL, *rev_obj; - - if (!PyArg_ParseTuple(args, ":get_revoked")) { - return NULL; - } - - num_rev = sk_X509_REVOKED_num(self->crl->crl->revoked); - if (num_rev < 0) { - Py_INCREF(Py_None); - return Py_None; - } - if ((obj = PyTuple_New(num_rev)) == NULL) { - return NULL; - } - - for (j = 0; j < num_rev; j++) { - r = sk_X509_REVOKED_value(self->crl->crl->revoked, j); - r = X509_REVOKED_dup(r); - if (r == NULL ) { - goto error; - } - rev_obj = (PyObject *) crypto_Revoked_New(r); - if (rev_obj == NULL) { - goto error; - } - r = NULL; /* it's now owned by rev_obj */ - PyTuple_SET_ITEM(obj, j, rev_obj); - } - return obj; - - error: - if (r) { - X509_REVOKED_free(r); - } - Py_XDECREF(obj); - return NULL; -} - -static char crypto_CRL_add_revoked_doc[] = "\n\ -Add a revoked (by value not reference) to the CRL structure\n\ -\n\ -@param cert: The new revoked.\n\ -@type cert: L{X509}\n\ -@return: None\n\ -"; -static PyObject * -crypto_CRL_add_revoked(crypto_CRLObj *self, PyObject *args, PyObject *keywds) { - crypto_RevokedObj * rev_obj = NULL; - static char *kwlist[] = {"revoked", NULL}; - X509_REVOKED * dup; - - if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!:add_revoked", - kwlist, &crypto_Revoked_Type, &rev_obj)) { - return NULL; - } - - dup = X509_REVOKED_dup( rev_obj->revoked ); - if (dup == NULL) { - return NULL; - } - X509_CRL_add0_revoked(self->crl, dup); - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_CRL_export_doc[] = "\n\ -export(cert, key[, type[, days]]) -> export a CRL as a string\n\ -\n\ -@param cert: Used to sign CRL.\n\ -@type cert: L{X509}\n\ -@param key: Used to sign CRL.\n\ -@type key: L{PKey}\n\ -@param type: The export format, either L{FILETYPE_PEM}, L{FILETYPE_ASN1}, or L{FILETYPE_TEXT}.\n\ -@param days: The number of days until the next update of this CRL.\n\ -@type days: L{int}\n\ -@return: L{str}\n\ -"; -static PyObject * -crypto_CRL_export(crypto_CRLObj *self, PyObject *args, PyObject *keywds) { - int ret, buf_len, type = X509_FILETYPE_PEM, days = 100; - char *temp; - BIO *bio; - PyObject *buffer; - crypto_PKeyObj *key; - ASN1_TIME *tmptm; - crypto_X509Obj *x509; - static char *kwlist[] = {"cert", "key", "type", "days", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!O!|ii:dump_crl", kwlist, - &crypto_X509_Type, &x509, - &crypto_PKey_Type, &key, &type, &days)) { - return NULL; - } - - bio = BIO_new(BIO_s_mem()); - tmptm = ASN1_TIME_new(); - if (!tmptm) { - return 0; - } - X509_gmtime_adj(tmptm,0); - X509_CRL_set_lastUpdate(self->crl, tmptm); - X509_gmtime_adj(tmptm,days*24*60*60); - X509_CRL_set_nextUpdate(self->crl, tmptm); - ASN1_TIME_free(tmptm); - X509_CRL_set_issuer_name(self->crl, X509_get_subject_name(x509->x509)); - X509_CRL_sign(self->crl, key->pkey, EVP_md5()); - switch (type) { - case X509_FILETYPE_PEM: - ret = PEM_write_bio_X509_CRL(bio, self->crl); - break; - - case X509_FILETYPE_ASN1: - ret = (int) i2d_X509_CRL_bio(bio, self->crl); - break; - - case X509_FILETYPE_TEXT: - ret = X509_CRL_print(bio, self->crl); - break; - - default: - PyErr_SetString( - PyExc_ValueError, - "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT"); - return NULL; - } - if (!ret) { - exception_from_error_queue(crypto_Error); - BIO_free(bio); - return NULL; - } - buf_len = BIO_get_mem_data(bio, &temp); - buffer = PyBytes_FromStringAndSize(temp, buf_len); - BIO_free(bio); - return buffer; -} - -crypto_CRLObj * -crypto_CRL_New(X509_CRL *crl) { - crypto_CRLObj *self; - - self = PyObject_New(crypto_CRLObj, &crypto_CRL_Type); - if (self == NULL) { - return NULL; - } - self->crl = crl; - return self; -} - -/* - * ADD_METHOD(name) expands to a correct PyMethodDef declaration - * { 'name', (PyCFunction)crypto_CRL_name, METH_VARARGS, crypto_CRL_name_doc } - * for convenience - */ -#define ADD_METHOD(name) \ - { #name, (PyCFunction)crypto_CRL_##name, METH_VARARGS, crypto_CRL_##name##_doc } -#define ADD_KW_METHOD(name) \ - { #name, (PyCFunction)crypto_CRL_##name, METH_VARARGS | METH_KEYWORDS, crypto_CRL_##name##_doc } -static PyMethodDef crypto_CRL_methods[] = { - ADD_KW_METHOD(add_revoked), - ADD_METHOD(get_revoked), - ADD_KW_METHOD(export), - { NULL, NULL } -}; -#undef ADD_METHOD - - -static void -crypto_CRL_dealloc(crypto_CRLObj *self) { - X509_CRL_free(self->crl); - self->crl = NULL; - - PyObject_Del(self); -} - -static char crypto_CRL_doc[] = "\n\ -CRL() -> CRL instance\n\ -\n\ -Create a new empty CRL object.\n\ -\n\ -@returns: The CRL object\n\ -"; - -static PyObject* crypto_CRL_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { - if (!PyArg_ParseTuple(args, ":CRL")) { - return NULL; - } - - return (PyObject *)crypto_CRL_New(X509_CRL_new()); -} - -PyTypeObject crypto_CRL_Type = { - PyOpenSSL_HEAD_INIT(&PyType_Type, 0) - "CRL", - sizeof(crypto_CRLObj), - 0, - (destructor)crypto_CRL_dealloc, - NULL, /* print */ - NULL, /* getattr */ - NULL, /* setattr */ - NULL, /* compare */ - NULL, /* repr */ - NULL, /* as_number */ - NULL, /* as_sequence */ - NULL, /* as_mapping */ - NULL, /* hash */ - NULL, /* call */ - NULL, /* str */ - NULL, /* getattro */ - NULL, /* setattro */ - NULL, /* as_buffer */ - Py_TPFLAGS_DEFAULT, - crypto_CRL_doc, /* doc */ - NULL, /* traverse */ - NULL, /* clear */ - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - crypto_CRL_methods, /* tp_methods */ - NULL, /* tp_members */ - NULL, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - NULL, /* tp_init */ - NULL, /* tp_alloc */ - crypto_CRL_new, /* tp_new */ -}; - -int init_crypto_crl(PyObject *module) { - if (PyType_Ready(&crypto_CRL_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_CRL_Type); - if (PyModule_AddObject(module, "CRL", (PyObject *)&crypto_CRL_Type) != 0) { - return 0; - } - return 1; -} diff --git a/OpenSSL/crypto/crl.h b/OpenSSL/crypto/crl.h deleted file mode 100644 index 87f5048..0000000 --- a/OpenSSL/crypto/crl.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PyOpenSSL_crypto_CRL_H_ -#define PyOpenSSL_crypto_CRL_H_ - -#include - -extern int init_crypto_crl (PyObject *); - -extern PyTypeObject crypto_CRL_Type; - -#define crypto_CRL_Check(v) ((v)->ob_type == &crypto_CRL_Type) - -typedef struct { - PyObject_HEAD - X509_CRL *crl; -} crypto_CRLObj; - -crypto_CRLObj * crypto_CRL_New(X509_CRL *crl); - -#endif diff --git a/OpenSSL/crypto/crypto.c b/OpenSSL/crypto/crypto.c deleted file mode 100644 index 3573a12..0000000 --- a/OpenSSL/crypto/crypto.c +++ /dev/null @@ -1,898 +0,0 @@ -/* - * crypto.c - * - * Copyright (C) AB Strakt - * Copyright (C) Keyphrene - * Copyright (C) Jean-Paul Calderone - * See LICENSE for details. - * - * Main file of crypto sub module. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - */ -#include -#define crypto_MODULE -#include "crypto.h" -#include "pkcs12.h" - -static char crypto_doc[] = "\n\ -Main file of crypto sub module.\n\ -See the file RATIONALE for a short explanation of why this module was written.\n\ -"; - -void **ssl_API; - -PyObject *crypto_Error; - -int crypto_byte_converter(PyObject *input, void* output) { - char **message = output; - if (input == Py_None) { - *message = NULL; - } else if (PyBytes_CheckExact(input)) { - *message = PyBytes_AsString(input); - } else { - return 0; - } - return 1; -} - -static int -global_passphrase_callback(char *buf, int len, int rwflag, void *cb_arg) -{ - PyObject *func, *argv, *ret; - int nchars; - - func = (PyObject *)cb_arg; - argv = Py_BuildValue("(i)", rwflag); - ret = PyEval_CallObject(func, argv); - Py_DECREF(argv); - if (ret == NULL) - return 0; - if (!PyBytes_Check(ret)) - { - PyErr_SetString(PyExc_ValueError, "String expected"); - return 0; - } - nchars = PyBytes_Size(ret); - if (nchars > len) - nchars = len; - strncpy(buf, PyBytes_AsString(ret), nchars); - return nchars; -} - -static char crypto_load_privatekey_doc[] = "\n\ -Load a private key from a buffer\n\ -\n\ -@param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\ -@param buffer: The buffer the key is stored in\n\ -@param passphrase: (optional) if encrypted PEM format, this can be\n\ - either the passphrase to use, or a callback for\n\ - providing the passphrase.\n\ -\n\ -@return: The PKey object\n\ -"; - -static PyObject * -crypto_load_privatekey(PyObject *spam, PyObject *args) -{ - crypto_PKeyObj *crypto_PKey_New(EVP_PKEY *, int); - int type, len; - char *buffer; - PyObject *pw = NULL; - pem_password_cb *cb = NULL; - void *cb_arg = NULL; - BIO *bio; - EVP_PKEY *pkey; - - if (!PyArg_ParseTuple(args, "is#|O:load_privatekey", &type, &buffer, &len, &pw)) - return NULL; - - if (pw != NULL) - { - if (PyBytes_Check(pw)) - { - cb = NULL; - cb_arg = PyBytes_AsString(pw); - } - else if (PyCallable_Check(pw)) - { - cb = global_passphrase_callback; - cb_arg = pw; - } - else - { - PyErr_SetString(PyExc_TypeError, "Last argument must be string or callable"); - return NULL; - } - } - - bio = BIO_new_mem_buf(buffer, len); - switch (type) - { - case X509_FILETYPE_PEM: - pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, cb_arg); - break; - - case X509_FILETYPE_ASN1: - pkey = d2i_PrivateKey_bio(bio, NULL); - break; - - default: - PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1"); - BIO_free(bio); - return NULL; - } - BIO_free(bio); - - if (pkey == NULL) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - return (PyObject *)crypto_PKey_New(pkey, 1); -} - -static char crypto_dump_privatekey_doc[] = "\n\ -Dump a private key to a buffer\n\ -\n\ -@param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\ -@param pkey: The PKey to dump\n\ -@param cipher: (optional) if encrypted PEM format, the cipher to\n\ - use\n\ -@param passphrase - (optional) if encrypted PEM format, this can be either\n\ - the passphrase to use, or a callback for providing the\n\ - passphrase.\n\ -@return: The buffer with the dumped key in\n\ -@rtype: C{str}\n\ -"; - -static PyObject * -crypto_dump_privatekey(PyObject *spam, PyObject *args) -{ - int type, ret, buf_len; - char *temp; - PyObject *buffer; - char *cipher_name = NULL; - const EVP_CIPHER *cipher = NULL; - PyObject *pw = NULL; - pem_password_cb *cb = NULL; - void *cb_arg = NULL; - BIO *bio; - RSA *rsa; - crypto_PKeyObj *pkey; - - if (!PyArg_ParseTuple(args, "iO!|sO:dump_privatekey", &type, - &crypto_PKey_Type, &pkey, &cipher_name, &pw)) - return NULL; - - if (cipher_name != NULL && pw == NULL) - { - PyErr_SetString(PyExc_ValueError, "Illegal number of arguments"); - return NULL; - } - if (cipher_name != NULL) - { - cipher = EVP_get_cipherbyname(cipher_name); - if (cipher == NULL) - { - PyErr_SetString(PyExc_ValueError, "Invalid cipher name"); - return NULL; - } - if (PyBytes_Check(pw)) - { - cb = NULL; - cb_arg = PyBytes_AsString(pw); - } - else if (PyCallable_Check(pw)) - { - cb = global_passphrase_callback; - cb_arg = pw; - } - else - { - PyErr_SetString(PyExc_TypeError, "Last argument must be string or callable"); - return NULL; - } - } - - bio = BIO_new(BIO_s_mem()); - switch (type) - { - case X509_FILETYPE_PEM: - ret = PEM_write_bio_PrivateKey(bio, pkey->pkey, cipher, NULL, 0, cb, cb_arg); - if (PyErr_Occurred()) - { - BIO_free(bio); - return NULL; - } - break; - - case X509_FILETYPE_ASN1: - ret = i2d_PrivateKey_bio(bio, pkey->pkey); - break; - - case X509_FILETYPE_TEXT: - rsa = EVP_PKEY_get1_RSA(pkey->pkey); - ret = RSA_print(bio, rsa, 0); - RSA_free(rsa); - break; - - default: - PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT"); - BIO_free(bio); - return NULL; - } - - if (ret == 0) - { - BIO_free(bio); - exception_from_error_queue(crypto_Error); - return NULL; - } - - buf_len = BIO_get_mem_data(bio, &temp); - buffer = PyBytes_FromStringAndSize(temp, buf_len); - BIO_free(bio); - - return buffer; -} - -static char crypto_load_certificate_doc[] = "\n\ -Load a certificate from a buffer\n\ -\n\ -@param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\ - buffer - The buffer the certificate is stored in\n\ -@return: The X509 object\n\ -"; - -static PyObject * -crypto_load_certificate(PyObject *spam, PyObject *args) -{ - crypto_X509Obj *crypto_X509_New(X509 *, int); - int type, len; - char *buffer; - BIO *bio; - X509 *cert; - - if (!PyArg_ParseTuple(args, "is#:load_certificate", &type, &buffer, &len)) - return NULL; - - bio = BIO_new_mem_buf(buffer, len); - switch (type) - { - case X509_FILETYPE_PEM: - cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); - break; - - case X509_FILETYPE_ASN1: - cert = d2i_X509_bio(bio, NULL); - break; - - default: - PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1"); - BIO_free(bio); - return NULL; - } - BIO_free(bio); - - if (cert == NULL) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - return (PyObject *)crypto_X509_New(cert, 1); -} - -static char crypto_dump_certificate_doc[] = "\n\ -Dump a certificate to a buffer\n\ -\n\ -@param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\ -@param cert: The certificate to dump\n\ -@return: The buffer with the dumped certificate in\n\ -"; - -static PyObject * -crypto_dump_certificate(PyObject *spam, PyObject *args) -{ - int type, ret, buf_len; - char *temp; - PyObject *buffer; - BIO *bio; - crypto_X509Obj *cert; - - if (!PyArg_ParseTuple(args, "iO!:dump_certificate", &type, - &crypto_X509_Type, &cert)) - return NULL; - - bio = BIO_new(BIO_s_mem()); - switch (type) - { - case X509_FILETYPE_PEM: - ret = PEM_write_bio_X509(bio, cert->x509); - break; - - case X509_FILETYPE_ASN1: - ret = i2d_X509_bio(bio, cert->x509); - break; - - case X509_FILETYPE_TEXT: - ret = X509_print_ex(bio, cert->x509, 0, 0); - break; - - default: - PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT"); - BIO_free(bio); - return NULL; - } - - if (ret == 0) - { - BIO_free(bio); - exception_from_error_queue(crypto_Error); - return NULL; - } - - buf_len = BIO_get_mem_data(bio, &temp); - buffer = PyBytes_FromStringAndSize(temp, buf_len); - BIO_free(bio); - - return buffer; -} - -static char crypto_load_certificate_request_doc[] = "\n\ -Load a certificate request from a buffer\n\ -\n\ -@param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\ - buffer - The buffer the certificate request is stored in\n\ -@return: The X509Req object\n\ -"; - -static PyObject * -crypto_load_certificate_request(PyObject *spam, PyObject *args) -{ - crypto_X509ReqObj *crypto_X509Req_New(X509_REQ *, int); - int type, len; - char *buffer; - BIO *bio; - X509_REQ *req; - - if (!PyArg_ParseTuple(args, "is#:load_certificate_request", &type, &buffer, &len)) - return NULL; - - bio = BIO_new_mem_buf(buffer, len); - switch (type) - { - case X509_FILETYPE_PEM: - req = PEM_read_bio_X509_REQ(bio, NULL, NULL, NULL); - break; - - case X509_FILETYPE_ASN1: - req = d2i_X509_REQ_bio(bio, NULL); - break; - - default: - PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1"); - BIO_free(bio); - return NULL; - } - BIO_free(bio); - - if (req == NULL) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - return (PyObject *)crypto_X509Req_New(req, 1); -} - -static char crypto_dump_certificate_request_doc[] = "\n\ -Dump a certificate request to a buffer\n\ -\n\ -@param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\ - req - The certificate request to dump\n\ -@return: The buffer with the dumped certificate request in\n\ -"; - -static PyObject * -crypto_dump_certificate_request(PyObject *spam, PyObject *args) -{ - int type, ret, buf_len; - char *temp; - PyObject *buffer; - BIO *bio; - crypto_X509ReqObj *req; - - if (!PyArg_ParseTuple(args, "iO!:dump_certificate_request", &type, - &crypto_X509Req_Type, &req)) - return NULL; - - bio = BIO_new(BIO_s_mem()); - switch (type) - { - case X509_FILETYPE_PEM: - ret = PEM_write_bio_X509_REQ(bio, req->x509_req); - break; - - case X509_FILETYPE_ASN1: - ret = i2d_X509_REQ_bio(bio, req->x509_req); - break; - - case X509_FILETYPE_TEXT: - ret = X509_REQ_print_ex(bio, req->x509_req, 0, 0); - break; - - default: - PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or FILETYPE_TEXT"); - BIO_free(bio); - return NULL; - } - - if (ret == 0) - { - BIO_free(bio); - exception_from_error_queue(crypto_Error); - return NULL; - } - - buf_len = BIO_get_mem_data(bio, &temp); - buffer = PyBytes_FromStringAndSize(temp, buf_len); - BIO_free(bio); - - return buffer; -} - -static char crypto_load_crl_doc[] = "\n\ -Load a certificate revocation list from a buffer\n\ -\n\ -@param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\ -@param buffer: The buffer the CRL is stored in\n\ -\n\ -@return: The PKey object\n\ -"; - -static PyObject * -crypto_load_crl(PyObject *spam, PyObject *args) { - int type, len; - char *buffer; - BIO *bio; - X509_CRL *crl; - - if (!PyArg_ParseTuple(args, "is#:load_crl", &type, &buffer, &len)) { - return NULL; - } - - bio = BIO_new_mem_buf(buffer, len); - switch (type) { - case X509_FILETYPE_PEM: - crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL); - break; - - case X509_FILETYPE_ASN1: - crl = d2i_X509_CRL_bio(bio, NULL); - break; - - default: - PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1"); - BIO_free(bio); - return NULL; - } - BIO_free(bio); - - if (crl == NULL) { - exception_from_error_queue(crypto_Error); - return NULL; - } - - return (PyObject *)crypto_CRL_New(crl); -} - -static char crypto_load_pkcs7_data_doc[] = "\n\ -Load pkcs7 data from a buffer\n\ -\n\ -@param type: The file type (one of FILETYPE_PEM or FILETYPE_ASN1)\n\ - buffer - The buffer with the pkcs7 data.\n\ -@return: The PKCS7 object\n\ -"; - -static PyObject * -crypto_load_pkcs7_data(PyObject *spam, PyObject *args) -{ - int type, len; - char *buffer; - BIO *bio; - PKCS7 *pkcs7 = NULL; - - if (!PyArg_ParseTuple(args, "is#:load_pkcs7_data", &type, &buffer, &len)) - return NULL; - - /* - * Try to read the pkcs7 data from the bio - */ - bio = BIO_new_mem_buf(buffer, len); - switch (type) - { - case X509_FILETYPE_PEM: - pkcs7 = PEM_read_bio_PKCS7(bio, NULL, NULL, NULL); - break; - - case X509_FILETYPE_ASN1: - pkcs7 = d2i_PKCS7_bio(bio, NULL); - break; - - default: - PyErr_SetString(PyExc_ValueError, - "type argument must be FILETYPE_PEM or FILETYPE_ASN1"); - return NULL; - } - BIO_free(bio); - - /* - * Check if we got a PKCS7 structure - */ - if (pkcs7 == NULL) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - return (PyObject *)crypto_PKCS7_New(pkcs7, 1); -} - -static char crypto_load_pkcs12_doc[] = "\n\ -Load a PKCS12 object from a buffer\n\ -\n\ -@param buffer: The buffer the certificate is stored in\n\ - passphrase (Optional) - The password to decrypt the PKCS12 lump\n\ -@returns: The PKCS12 object\n\ -"; - -static PyObject * -crypto_load_pkcs12(PyObject *spam, PyObject *args) -{ - int len; - char *buffer, *passphrase = NULL; - BIO *bio; - PKCS12 *p12; - - if (!PyArg_ParseTuple(args, "s#|s:load_pkcs12", &buffer, &len, &passphrase)) - return NULL; - - bio = BIO_new_mem_buf(buffer, len); - if ((p12 = d2i_PKCS12_bio(bio, NULL)) == NULL) - { - BIO_free(bio); - exception_from_error_queue(crypto_Error); - return NULL; - } - BIO_free(bio); - - return (PyObject *)crypto_PKCS12_New(p12, passphrase); -} - - -static char crypto_X509_verify_cert_error_string_doc[] = "\n\ -Get X509 verify certificate error string.\n\ -\n\ -@param errnum: The error number.\n\ -@return: Error string as a Python string\n\ -"; - -static PyObject * -crypto_X509_verify_cert_error_string(PyObject *spam, PyObject *args) -{ - int errnum; - const char *str; - - if (!PyArg_ParseTuple(args, "i", &errnum)) - return NULL; - - str = X509_verify_cert_error_string(errnum); - return PyText_FromString(str); -} - -static char crypto_exception_from_error_queue_doc[] = "\n\ -Raise an exception from the current OpenSSL error queue.\n\ -"; - -static PyObject * -crypto_exception_from_error_queue(PyObject *spam, PyObject *eggs) { - exception_from_error_queue(crypto_Error); - return NULL; -} - -static char crypto_sign_doc[] = "\n\ -Sign data with a digest\n\ -\n\ -@param pkey: Pkey to sign with\n\ -@param data: data to be signed\n\ -@param digest: message digest to use\n\ -@return: signature\n\ -"; - -static PyObject * -crypto_sign(PyObject *spam, PyObject *args) { - PyObject *buffer; - crypto_PKeyObj *pkey; - char *data = NULL; - int data_len; - char *digest_name; - int err; - unsigned int sig_len; - const EVP_MD *digest; - EVP_MD_CTX md_ctx; - unsigned char sig_buf[512]; - - if (!PyArg_ParseTuple( - args, "O!" BYTESTRING_FMT "#s:sign", &crypto_PKey_Type, - &pkey, &data, &data_len, &digest_name)) { - return NULL; - } - - if ((digest = EVP_get_digestbyname(digest_name)) == NULL) { - PyErr_SetString(PyExc_ValueError, "No such digest method"); - return NULL; - } - - EVP_SignInit(&md_ctx, digest); - EVP_SignUpdate(&md_ctx, data, data_len); - sig_len = sizeof(sig_buf); - err = EVP_SignFinal(&md_ctx, sig_buf, &sig_len, pkey->pkey); - - if (err != 1) { - exception_from_error_queue(crypto_Error); - return NULL; - } - - buffer = PyBytes_FromStringAndSize((char*)sig_buf, sig_len); - return buffer; -} - -static char crypto_verify_doc[] = "\n\ -Verify a signature\n\ -\n\ -@param cert: signing certificate (X509 object)\n\ -@param signature: signature returned by sign function\n\ -@param data: data to be verified\n\ -@param digest: message digest to use\n\ -@return: None if the signature is correct, raise exception otherwise\n\ -"; - -static PyObject * -crypto_verify(PyObject *spam, PyObject *args) { - crypto_X509Obj *cert; - unsigned char *signature; - int sig_len; - char *data, *digest_name; - int data_len; - int err; - const EVP_MD *digest; - EVP_MD_CTX md_ctx; - EVP_PKEY *pkey; - -#ifdef PY3 - if (!PyArg_ParseTuple(args, "O!" BYTESTRING_FMT "#" BYTESTRING_FMT "#s:verify", &crypto_X509_Type, &cert, &signature, &sig_len, &data, &data_len, &digest_name)) { -#else - if (!PyArg_ParseTuple(args, "O!t#s#s:verify", &crypto_X509_Type, &cert, &signature, &sig_len, &data, &data_len, &digest_name)) { -#endif - return NULL; - } - - if ((digest = EVP_get_digestbyname(digest_name)) == NULL){ - PyErr_SetString(PyExc_ValueError, "No such digest method"); - return NULL; - } - - pkey = X509_get_pubkey(cert->x509); - if (pkey == NULL) { - PyErr_SetString(PyExc_ValueError, "No public key"); - return NULL; - } - - EVP_VerifyInit(&md_ctx, digest); - EVP_VerifyUpdate(&md_ctx, data, data_len); - err = EVP_VerifyFinal(&md_ctx, signature, sig_len, pkey); - EVP_PKEY_free(pkey); - - if (err != 1) { - exception_from_error_queue(crypto_Error); - return NULL; - } - - Py_INCREF(Py_None); - return Py_None; -} - -/* Methods in the OpenSSL.crypto module (i.e. none) */ -static PyMethodDef crypto_methods[] = { - /* Module functions */ - { "load_privatekey", (PyCFunction)crypto_load_privatekey, METH_VARARGS, crypto_load_privatekey_doc }, - { "dump_privatekey", (PyCFunction)crypto_dump_privatekey, METH_VARARGS, crypto_dump_privatekey_doc }, - { "load_certificate", (PyCFunction)crypto_load_certificate, METH_VARARGS, crypto_load_certificate_doc }, - { "dump_certificate", (PyCFunction)crypto_dump_certificate, METH_VARARGS, crypto_dump_certificate_doc }, - { "load_certificate_request", (PyCFunction)crypto_load_certificate_request, METH_VARARGS, crypto_load_certificate_request_doc }, - { "dump_certificate_request", (PyCFunction)crypto_dump_certificate_request, METH_VARARGS, crypto_dump_certificate_request_doc }, - { "load_crl", (PyCFunction)crypto_load_crl, METH_VARARGS, crypto_load_crl_doc }, - { "load_pkcs7_data", (PyCFunction)crypto_load_pkcs7_data, METH_VARARGS, crypto_load_pkcs7_data_doc }, - { "load_pkcs12", (PyCFunction)crypto_load_pkcs12, METH_VARARGS, crypto_load_pkcs12_doc }, - { "sign", (PyCFunction)crypto_sign, METH_VARARGS, crypto_sign_doc }, - { "verify", (PyCFunction)crypto_verify, METH_VARARGS, crypto_verify_doc }, - { "X509_verify_cert_error_string", (PyCFunction)crypto_X509_verify_cert_error_string, METH_VARARGS, crypto_X509_verify_cert_error_string_doc }, - { "_exception_from_error_queue", (PyCFunction)crypto_exception_from_error_queue, METH_NOARGS, crypto_exception_from_error_queue_doc }, - { NULL, NULL } -}; - - -#ifdef WITH_THREAD - -#include - -/** - * 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((unsigned long (*)(void))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 - -#ifdef PY3 -static struct PyModuleDef cryptomodule = { - PyModuleDef_HEAD_INIT, - "crypto", - crypto_doc, - -1, - crypto_methods -}; -#endif - -/* - * Initialize crypto sub module - * - * Arguments: None - * Returns: None - */ -PyOpenSSL_MODINIT(crypto) { -#ifndef PY3 - static void *crypto_API[crypto_API_pointers]; - PyObject *c_api_object; -#endif - PyObject *module; - - ERR_load_crypto_strings(); - OpenSSL_add_all_algorithms(); - -#ifdef PY3 - module = PyModule_Create(&cryptomodule); -#else - module = Py_InitModule3("crypto", crypto_methods, crypto_doc); -#endif - - if (module == NULL) { - PyOpenSSL_MODRETURN(NULL); - } - -#ifndef PY3 - /* Initialize the C API pointer array */ - crypto_API[crypto_X509_New_NUM] = (void *)crypto_X509_New; - crypto_API[crypto_X509Name_New_NUM] = (void *)crypto_X509Name_New; - crypto_API[crypto_X509Req_New_NUM] = (void *)crypto_X509Req_New; - crypto_API[crypto_X509Store_New_NUM] = (void *)crypto_X509Store_New; - crypto_API[crypto_PKey_New_NUM] = (void *)crypto_PKey_New; - crypto_API[crypto_X509Extension_New_NUM] = (void *)crypto_X509Extension_New; - crypto_API[crypto_PKCS7_New_NUM] = (void *)crypto_PKCS7_New; - crypto_API[crypto_NetscapeSPKI_New_NUM] = (void *)crypto_NetscapeSPKI_New; - c_api_object = PyCObject_FromVoidPtr((void *)crypto_API, NULL); - if (c_api_object != NULL) { - /* PyModule_AddObject steals a reference. - */ - Py_INCREF(c_api_object); - PyModule_AddObject(module, "_C_API", c_api_object); - } -#endif - - crypto_Error = PyErr_NewException("OpenSSL.crypto.Error", NULL, NULL); - if (crypto_Error == NULL) - goto error; - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF(crypto_Error); - if (PyModule_AddObject(module, "Error", crypto_Error) != 0) - goto error; - - PyModule_AddIntConstant(module, "FILETYPE_PEM", X509_FILETYPE_PEM); - PyModule_AddIntConstant(module, "FILETYPE_ASN1", X509_FILETYPE_ASN1); - PyModule_AddIntConstant(module, "FILETYPE_TEXT", X509_FILETYPE_TEXT); - - PyModule_AddIntConstant(module, "TYPE_RSA", crypto_TYPE_RSA); - PyModule_AddIntConstant(module, "TYPE_DSA", crypto_TYPE_DSA); - -#ifdef WITH_THREAD - if (!init_openssl_threads()) - goto error; -#endif - if (!init_crypto_x509(module)) - goto error; - if (!init_crypto_x509name(module)) - goto error; - if (!init_crypto_x509store(module)) - goto error; - if (!init_crypto_x509req(module)) - goto error; - if (!init_crypto_pkey(module)) - goto error; - if (!init_crypto_x509extension(module)) - goto error; - if (!init_crypto_pkcs7(module)) - goto error; - if (!init_crypto_pkcs12(module)) - goto error; - if (!init_crypto_netscape_spki(module)) - goto error; - if (!init_crypto_crl(module)) - goto error; - if (!init_crypto_revoked(module)) - goto error; - - PyOpenSSL_MODRETURN(module); - -error: - PyOpenSSL_MODRETURN(NULL); - ; -} diff --git a/OpenSSL/crypto/crypto.h b/OpenSSL/crypto/crypto.h deleted file mode 100644 index 4006e71..0000000 --- a/OpenSSL/crypto/crypto.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * crypto.h - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * Exports from crypto.c. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - * - */ -#ifndef PyOpenSSL_CRYPTO_H_ -#define PyOpenSSL_CRYPTO_H_ - -#include -/* Work around a bug in OpenSSL 1.0.0 which is caused by winsock.h being - included (from dtls1.h) too late by the OpenSSL header files, overriding - the fixes (in ossl_typ.h) for symbol clashes caused by this OS header - file. - - In order to have those fixes still take effect, we include winsock.h - here, prior to including any OpenSSL header files. - - */ -#ifdef _WIN32 -# include "winsock.h" -#endif - -#include "x509.h" -#include "x509name.h" -#include "netscape_spki.h" -#include "x509store.h" -#include "x509req.h" -#include "pkey.h" -#include "x509ext.h" -#include "pkcs7.h" -#include "pkcs12.h" -#include "crl.h" -#include "revoked.h" -#include "../util.h" - -extern PyObject *crypto_Error; - -#define crypto_X509_New_NUM 0 -#define crypto_X509_New_RETURN crypto_X509Obj * -#define crypto_X509_New_PROTO (X509 *, int) - -#define crypto_X509Req_New_NUM 1 -#define crypto_X509Req_New_RETURN crypto_X509ReqObj * -#define crypto_X509Req_New_PROTO (X509_REQ *, int) - -#define crypto_X509Store_New_NUM 2 -#define crypto_X509Store_New_RETURN crypto_X509StoreObj * -#define crypto_X509Store_New_PROTO (X509_STORE *, int) - -#define crypto_PKey_New_NUM 3 -#define crypto_PKey_New_RETURN crypto_PKeyObj * -#define crypto_PKey_New_PROTO (EVP_PKEY *, int) - -#define crypto_X509Name_New_NUM 4 -#define crypto_X509Name_New_RETURN crypto_X509NameObj * -#define crypto_X509Name_New_PROTO (X509_NAME *, int) - -#define crypto_X509Extension_New_NUM 5 -#define crypto_X509Extension_New_RETURN crypto_X509ExtensionObj * -#define crypto_X509Extension_New_PROTO (char *, int, char *, crypto_X509Obj *, crypto_X509Obj *) - -#define crypto_PKCS7_New_NUM 6 -#define crypto_PKCS7_New_RETURN crypto_PKCS7Obj * -#define crypto_PKCS7_New_PROTO (PKCS7 *, int) - -#define crypto_NetscapeSPKI_New_NUM 7 -#define crypto_NetscapeSPKI_New_RETURN crypto_NetscapeSPKIObj * -#define crypto_NetscapeSPKI_New_PROTO (NETSCAPE_SPKI *, int) - -#define crypto_API_pointers 8 - -#if defined(PY3) || defined(crypto_MODULE) - -#ifdef _WIN32 -#define EXPORT __declspec(dllexport) -#else -#define EXPORT -#endif - -extern EXPORT crypto_X509_New_RETURN crypto_X509_New crypto_X509_New_PROTO; -extern EXPORT crypto_X509Name_New_RETURN crypto_X509Name_New crypto_X509Name_New_PROTO; -extern crypto_X509Req_New_RETURN crypto_X509Req_New crypto_X509Req_New_PROTO; -extern EXPORT crypto_X509Store_New_RETURN crypto_X509Store_New crypto_X509Store_New_PROTO; -extern crypto_PKey_New_RETURN crypto_PKey_New crypto_PKey_New_PROTO; -extern crypto_X509Extension_New_RETURN crypto_X509Extension_New crypto_X509Extension_New_PROTO; -extern crypto_PKCS7_New_RETURN crypto_PKCS7_New crypto_PKCS7_New_PROTO; -extern crypto_NetscapeSPKI_New_RETURN crypto_NetscapeSPKI_New crypto_NetscapeSPKI_New_PROTO; - -int crypto_byte_converter(PyObject *input, void *output); - -#else /* crypto_MODULE */ - -extern void **crypto_API; - -#define crypto_X509_New \ - (*(crypto_X509_New_RETURN (*)crypto_X509_New_PROTO) crypto_API[crypto_X509_New_NUM]) -#define crypto_X509Name_New \ - (*(crypto_X509Name_New_RETURN (*)crypto_X509Name_New_PROTO) crypto_API[crypto_X509Name_New_NUM]) -#define crypto_X509Req_New \ - (*(crypto_X509Req_New_RETURN (*)crypto_X509Req_New_PROTO) crypto_API[crypto_X509Req_New_NUM]) -#define crypto_X509Store_New \ - (*(crypto_X509Store_New_RETURN (*)crypto_X509Store_New_PROTO) crypto_API[crypto_X509Store_New_NUM]) -#define crypto_PKey_New \ - (*(crypto_PKey_New_RETURN (*)crypto_PKey_New_PROTO) crypto_API[crypto_PKey_New_NUM]) -#define crypto_X509Extension_New\ - (*(crypto_X509Extension_New_RETURN (*)crypto_X509Extension_New_PROTO) crypto_API[crypto_X509Extension_New_NUM]) -#define crypto_PKCS7_New \ - (*(crypto_PKCS7_New_RETURN (*)crypto_PKCS7_New_PROTO) crypto_API[crypto_PKCS7_New_NUM]) -#define crypto_NetscapeSPKI_New \ - (*(crypto_NetscapeSPKI_New_RETURN (*)crypto_NetscapeSPKI_New_PROTO) crypto_API[crypto_NetscapeSPKI_New_NUM]) - -#define import_crypto() \ -{ \ - PyObject *crypto_module = PyImport_ImportModule("OpenSSL.crypto"); \ - if (crypto_module != NULL) { \ - PyObject *crypto_dict, *crypto_api_object; \ - crypto_dict = PyModule_GetDict(crypto_module); \ - crypto_api_object = PyDict_GetItemString(crypto_dict, "_C_API"); \ - if (crypto_api_object && PyCObject_Check(crypto_api_object)) { \ - crypto_API = (void **)PyCObject_AsVoidPtr(crypto_api_object); \ - } \ - } \ -} - -#endif /* crypto_MODULE */ - -/* Define a new type for emitting text. Hopefully these don't collide with - * future official OpenSSL constants, but the switch statement of - * dump_certificate() will alert us if it matters. - */ -#ifndef X509_FILETYPE_TEXT -#define X509_FILETYPE_TEXT (58) -#endif - -#endif /* PyOpenSSL_CRYPTO_H_ */ diff --git a/OpenSSL/crypto/netscape_spki.c b/OpenSSL/crypto/netscape_spki.c deleted file mode 100644 index 9369d50..0000000 --- a/OpenSSL/crypto/netscape_spki.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * netscape_spki.c - * - * Copyright (C) Tollef Fog Heen - * See LICENSE for details. - * - * Netscape SPKI handling, thin wrapper - */ -#include -#define crypto_MODULE -#include "crypto.h" - -/* - * Constructor for Nestcape_SPKI, never called by Python code directly - * - * Arguments: name - A "real" NetscapeSPKI object - * dealloc - Boolean value to specify whether the destructor should - * free the "real" NetscapeSPKI object - * Returns: The newly created NetscapeSPKI object - */ -crypto_NetscapeSPKIObj * -crypto_NetscapeSPKI_New(NETSCAPE_SPKI *name, int dealloc) -{ - crypto_NetscapeSPKIObj *self; - - self = PyObject_New(crypto_NetscapeSPKIObj, &crypto_NetscapeSPKI_Type); - - if (self == NULL) - return NULL; - - self->netscape_spki = name; - self->dealloc = dealloc; - - return self; -} - - -static char crypto_NetscapeSPKI_doc[] = "\n\ -NetscapeSPKI([enc]) -> NetscapeSPKI instance\n\ -\n\ -@param enc: Base64 encoded NetscapeSPKI object.\n\ -@type enc: C{str}\n\ -@return: The NetscapeSPKI object\n\ -"; - -static PyObject * -crypto_NetscapeSPKI_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { - char *enc = NULL; - int enc_len = -1; - NETSCAPE_SPKI *spki; - - if (!PyArg_ParseTuple(args, "|s#:NetscapeSPKI", &enc, &enc_len)) - return NULL; - - if (enc_len >= 0) - spki = NETSCAPE_SPKI_b64_decode(enc, enc_len); - else - spki = NETSCAPE_SPKI_new(); - if (spki == NULL) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - return (PyObject *)crypto_NetscapeSPKI_New(spki, 1); -} - - -/* - * Deallocate the memory used by the NetscapeSPKI object - * - * Arguments: self - The NetscapeSPKI object - * Returns: None - */ -static void -crypto_NetscapeSPKI_dealloc(crypto_NetscapeSPKIObj *self) -{ - /* Sometimes we don't have to dealloc this */ - if (self->dealloc) - NETSCAPE_SPKI_free(self->netscape_spki); - - PyObject_Del(self); -} - -static char crypto_NetscapeSPKI_sign_doc[] = "\n\ -Sign the certificate request using the supplied key and digest\n\ -\n\ -@param pkey: The key to sign with\n\ -@param digest: The message digest to use\n\ -@return: None\n\ -"; - -static PyObject * -crypto_NetscapeSPKI_sign(crypto_NetscapeSPKIObj *self, PyObject *args) -{ - crypto_PKeyObj *pkey; - char *digest_name; - const EVP_MD *digest; - - if (!PyArg_ParseTuple(args, "O!s:sign", &crypto_PKey_Type, &pkey, - &digest_name)) - return NULL; - - if (pkey->only_public) { - PyErr_SetString(PyExc_ValueError, "Key has only public part"); - return NULL; - } - - if (!pkey->initialized) { - PyErr_SetString(PyExc_ValueError, "Key is uninitialized"); - return NULL; - } - - if ((digest = EVP_get_digestbyname(digest_name)) == NULL) - { - PyErr_SetString(PyExc_ValueError, "No such digest method"); - return NULL; - } - - if (!NETSCAPE_SPKI_sign(self->netscape_spki, pkey->pkey, digest)) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_NetscapeSPKI_verify_doc[] = "\n\ -Verifies a certificate request using the supplied public key\n\ -\n\ -@param key: a public key\n\ -@return: True if the signature is correct.\n\ -@raise OpenSSL.crypto.Error: If the signature is invalid or there is a\n\ - problem verifying the signature.\n\ -"; - -PyObject * -crypto_NetscapeSPKI_verify(crypto_NetscapeSPKIObj *self, PyObject *args) -{ - crypto_PKeyObj *pkey; - int answer; - - if (!PyArg_ParseTuple(args, "O!:verify", &crypto_PKey_Type, &pkey)) { - return NULL; - } - - if ((answer = NETSCAPE_SPKI_verify(self->netscape_spki, pkey->pkey)) <= 0) { - exception_from_error_queue(crypto_Error); - return NULL; - } - - return PyLong_FromLong((long)answer); -} - -static char crypto_NetscapeSPKI_b64_encode_doc[] = "\n\ -Generate a base64 encoded string from an SPKI\n\ -\n\ -@return: The base64 encoded string\n\ -"; - -PyObject * -crypto_NetscapeSPKI_b64_encode(crypto_NetscapeSPKIObj *self, PyObject *args) -{ - char *str; - - if (!PyArg_ParseTuple(args, ":b64_encode")) - return NULL; - - str = NETSCAPE_SPKI_b64_encode(self->netscape_spki); - return PyBytes_FromString(str); -} - - -static char crypto_NetscapeSPKI_get_pubkey_doc[] = "\n\ -Get the public key of the certificate\n\ -\n\ -@return: The public key\n\ -"; - -static PyObject * -crypto_NetscapeSPKI_get_pubkey(crypto_NetscapeSPKIObj *self, PyObject *args) -{ - crypto_PKeyObj *crypto_PKey_New(EVP_PKEY *, int); - EVP_PKEY *pkey; - crypto_PKeyObj *py_pkey; - - if (!PyArg_ParseTuple(args, ":get_pubkey")) - return NULL; - - if ((pkey = NETSCAPE_SPKI_get_pubkey(self->netscape_spki)) == NULL) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - py_pkey = crypto_PKey_New(pkey, 1); - if (py_pkey != NULL) { - py_pkey->only_public = 1; - } - return (PyObject *)py_pkey; -} - -static char crypto_NetscapeSPKI_set_pubkey_doc[] = "\n\ -Set the public key of the certificate\n\ -\n\ -@param pkey: The public key\n\ -@return: None\n\ -"; - -static PyObject * -crypto_NetscapeSPKI_set_pubkey(crypto_NetscapeSPKIObj *self, PyObject *args) -{ - crypto_PKeyObj *pkey; - - if (!PyArg_ParseTuple(args, "O!:set_pubkey", &crypto_PKey_Type, &pkey)) - return NULL; - - if (!NETSCAPE_SPKI_set_pubkey(self->netscape_spki, pkey->pkey)) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - Py_INCREF(Py_None); - return Py_None; -} - -/* - * ADD_METHOD(name) expands to a correct PyMethodDef declaration - * { 'name', (PyCFunction)crypto_NetscapeSPKI_name, METH_VARARGS } - * for convenience - */ -#define ADD_METHOD(name) \ - { #name, (PyCFunction)crypto_NetscapeSPKI_##name, METH_VARARGS, crypto_NetscapeSPKI_##name##_doc } -static PyMethodDef crypto_NetscapeSPKI_methods[] = -{ - ADD_METHOD(get_pubkey), - ADD_METHOD(set_pubkey), - ADD_METHOD(b64_encode), - ADD_METHOD(sign), - ADD_METHOD(verify), - { NULL, NULL } -}; -#undef ADD_METHOD - -PyTypeObject crypto_NetscapeSPKI_Type = { - PyOpenSSL_HEAD_INIT(&PyType_Type, 0) - "NetscapeSPKI", - sizeof(crypto_NetscapeSPKIObj), - 0, - (destructor)crypto_NetscapeSPKI_dealloc, - NULL, /* print */ - NULL, /* getattr */ - NULL, /* setattr */ - NULL, /* compare */ - NULL, /* repr */ - NULL, /* as_number */ - NULL, /* as_sequence */ - NULL, /* as_mapping */ - NULL, /* hash */ - NULL, /* call */ - NULL, /* str */ - NULL, /* getattro */ - NULL, /* setattro */ - NULL, /* as_buffer */ - Py_TPFLAGS_DEFAULT, - crypto_NetscapeSPKI_doc, /* doc */ - NULL, /* traverse */ - NULL, /* clear */ - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - crypto_NetscapeSPKI_methods, /* tp_methods */ - NULL, /* tp_members */ - NULL, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - NULL, /* tp_init */ - NULL, /* tp_alloc */ - crypto_NetscapeSPKI_new, /* tp_new */ -}; - - -/* - * Initialize the X509Name part of the crypto module - * - * Arguments: module - The crypto module - * Returns: None - */ -int -init_crypto_netscape_spki(PyObject *module) { - if (PyType_Ready(&crypto_NetscapeSPKI_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference - */ - Py_INCREF((PyObject *)&crypto_NetscapeSPKI_Type); - if (PyModule_AddObject(module, "NetscapeSPKI", (PyObject *)&crypto_NetscapeSPKI_Type) != 0) { - return 0; - } - - /* PyModule_AddObject steals a reference - */ - Py_INCREF((PyObject *)&crypto_NetscapeSPKI_Type); - if (PyModule_AddObject(module, "NetscapeSPKIType", (PyObject *)&crypto_NetscapeSPKI_Type) != 0) { - return 0; - } - - return 1; -} diff --git a/OpenSSL/crypto/netscape_spki.h b/OpenSSL/crypto/netscape_spki.h deleted file mode 100644 index 2f07307..0000000 --- a/OpenSSL/crypto/netscape_spki.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * netscape_spki.h - * - * Copyright (C) Tollef Fog Heen - * See LICENSE for details. - * - * Handle Netscape SPKI (challenge response) certificate requests. - * - * - */ -#ifndef PyOpenSSL_crypto_Netscape_SPKI_H_ -#define PyOpenSSL_crypto_Netscape_SPKI_H_ - -#include -#include - -extern int init_crypto_netscape_spki (PyObject *); - -extern PyTypeObject crypto_NetscapeSPKI_Type; - -#define crypto_NetscapeSPKI_Check(v) ((v)->ob_type == &crypto_NetscapeSPKI_Type) - -typedef struct { - PyObject_HEAD - NETSCAPE_SPKI *netscape_spki; - int dealloc; -} crypto_NetscapeSPKIObj; - - -#endif diff --git a/OpenSSL/crypto/pkcs12.c b/OpenSSL/crypto/pkcs12.c deleted file mode 100644 index a1a5a79..0000000 --- a/OpenSSL/crypto/pkcs12.c +++ /dev/null @@ -1,580 +0,0 @@ -/* - * pkcs12.c - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * Certificate transport (PKCS12) handling code, - * mostly thin wrappers around OpenSSL. - * See the file RATIONALE for a short explanation of why - * this module was written. - * - * Reviewed 2001-07-23 - */ -#include -#define crypto_MODULE -#include "crypto.h" - -/* - * PKCS12 is a standard exchange format for digital certificates. - * See e.g. the OpenSSL homepage http://www.openssl.org/ for more information - */ - -static void crypto_PKCS12_dealloc(crypto_PKCS12Obj *self); -static int crypto_PKCS12_clear(crypto_PKCS12Obj *self); - -static char crypto_PKCS12_get_certificate_doc[] = "\n\ -Return certificate portion of the PKCS12 structure\n\ -\n\ -@return: X509 object containing the certificate\n\ -"; -static PyObject * -crypto_PKCS12_get_certificate(crypto_PKCS12Obj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":get_certificate")) - return NULL; - - Py_INCREF(self->cert); - return self->cert; -} - -static char crypto_PKCS12_set_certificate_doc[] = "\n\ -Replace the certificate portion of the PKCS12 structure\n\ -\n\ -@param cert: The new certificate.\n\ -@type cert: L{X509} or L{NoneType}\n\ -@return: None\n\ -"; -static PyObject * -crypto_PKCS12_set_certificate(crypto_PKCS12Obj *self, PyObject *args, PyObject *keywds) { - PyObject *cert = NULL; - static char *kwlist[] = {"cert", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, keywds, "O:set_certificate", - kwlist, &cert)) - return NULL; - - if (cert != Py_None && ! crypto_X509_Check(cert)) { - PyErr_SetString(PyExc_TypeError, "cert must be type X509 or None"); - return NULL; - } - - Py_INCREF(cert); /* Make consistent before calling Py_DECREF() */ - Py_DECREF(self->cert); - self->cert = cert; - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_PKCS12_get_privatekey_doc[] = "\n\ -Return private key portion of the PKCS12 structure\n\ -\n\ -@returns: PKey object containing the private key\n\ -"; -static crypto_PKeyObj * -crypto_PKCS12_get_privatekey(crypto_PKCS12Obj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":get_privatekey")) - return NULL; - - Py_INCREF(self->key); - return (crypto_PKeyObj *) self->key; -} - -static char crypto_PKCS12_set_privatekey_doc[] = "\n\ -Replace or set the certificate portion of the PKCS12 structure\n\ -\n\ -@param pkey: The new private key.\n\ -@type pkey: L{PKey}\n\ -@return: None\n\ -"; -static PyObject * -crypto_PKCS12_set_privatekey(crypto_PKCS12Obj *self, PyObject *args, PyObject *keywds) { - PyObject *pkey = NULL; - static char *kwlist[] = {"pkey", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, keywds, "O:set_privatekey", - kwlist, &pkey)) - return NULL; - - if (pkey != Py_None && ! crypto_PKey_Check(pkey)) { - PyErr_SetString(PyExc_TypeError, "pkey must be type X509 or None"); - return NULL; - } - - Py_INCREF(pkey); /* Make consistent before calling Py_DECREF() */ - Py_DECREF(self->key); - self->key = pkey; - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_PKCS12_get_ca_certificates_doc[] = "\n\ -Return CA certificates within of the PKCS12 object\n\ -\n\ -@return: A newly created tuple containing the CA certificates in the chain,\n\ - if any are present, or None if no CA certificates are present.\n\ -"; -static PyObject * -crypto_PKCS12_get_ca_certificates(crypto_PKCS12Obj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":get_ca_certificates")) - return NULL; - - Py_INCREF(self->cacerts); - return self->cacerts; -} - -static char crypto_PKCS12_set_ca_certificates_doc[] = "\n\ -Replace or set the CA certificates withing the PKCS12 object.\n\ -\n\ -@param cacerts: The new CA certificates.\n\ -@type cacerts: Iterable of L{X509} or L{NoneType}\n\ -@return: None\n\ -"; -static PyObject * -crypto_PKCS12_set_ca_certificates(crypto_PKCS12Obj *self, PyObject *args, PyObject *keywds) -{ - PyObject *obj; - PyObject *cacerts; - static char *kwlist[] = {"cacerts", NULL}; - int i, len; /* Py_ssize_t for Python 2.5+ */ - - if (!PyArg_ParseTupleAndKeywords(args, keywds, "O:set_ca_certificates", - kwlist, &cacerts)) - return NULL; - if (cacerts == Py_None) { - Py_INCREF(cacerts); - } else { - /* It's iterable */ - cacerts = PySequence_Tuple(cacerts); - if (cacerts == NULL) { - return NULL; - } - len = PyTuple_Size(cacerts); - - /* Check is's a simple list filled only with X509 objects. */ - for (i = 0; i < len; i++) { - obj = PyTuple_GetItem(cacerts, i); - if (!crypto_X509_Check(obj)) { - Py_DECREF(cacerts); - PyErr_SetString(PyExc_TypeError, "iterable must only contain X509Type"); - return NULL; - } - } - } - - Py_DECREF(self->cacerts); - self->cacerts = cacerts; - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_PKCS12_get_friendlyname_doc[] = "\n\ -Return friendly name portion of the PKCS12 structure\n\ -\n\ -@returns: String containing the friendlyname\n\ -"; -static PyObject * -crypto_PKCS12_get_friendlyname(crypto_PKCS12Obj *self, PyObject *args) { - if (!PyArg_ParseTuple(args, ":get_friendlyname")) - return NULL; - - Py_INCREF(self->friendlyname); - return (PyObject *) self->friendlyname; -} - -static char crypto_PKCS12_set_friendlyname_doc[] = "\n\ -Replace or set the certificate portion of the PKCS12 structure\n\ -\n\ -@param name: The new friendly name.\n\ -@type name: L{str}\n\ -@return: None\n\ -"; -static PyObject * -crypto_PKCS12_set_friendlyname(crypto_PKCS12Obj *self, PyObject *args, PyObject *keywds) { - PyObject *name = NULL; - static char *kwlist[] = {"name", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, keywds, "O:set_friendlyname", - kwlist, &name)) - return NULL; - - if (name != Py_None && ! PyBytes_CheckExact(name)) { - PyErr_SetString(PyExc_TypeError, "name must be a byte string or None"); - return NULL; - } - - Py_INCREF(name); /* Make consistent before calling Py_DECREF() */ - Py_DECREF(self->friendlyname); - self->friendlyname = name; - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_PKCS12_export_doc[] = "\n\ -export([passphrase=None][, friendly_name=None][, iter=2048][, maciter=1]\n\ -Dump a PKCS12 object as a string. See also \"man PKCS12_create\".\n\ -\n\ -@param passphrase: used to encrypt the PKCS12\n\ -@type passphrase: L{str}\n\ -@param iter: How many times to repeat the encryption\n\ -@type iter: L{int}\n\ -@param maciter: How many times to repeat the MAC\n\ -@type maciter: L{int}\n\ -@return: The string containing the PKCS12\n\ -"; -static PyObject * -crypto_PKCS12_export(crypto_PKCS12Obj *self, PyObject *args, PyObject *keywds) { - int i; /* Py_ssize_t for Python 2.5+ */ - PyObject *obj; - int buf_len; - PyObject *buffer; - char *temp, *passphrase = NULL, *friendly_name = NULL; - BIO *bio; - PKCS12 *p12; - EVP_PKEY *pkey = NULL; - STACK_OF(X509) *cacerts = NULL; - X509 *x509 = NULL; - int iter = 0; /* defaults to PKCS12_DEFAULT_ITER */ - int maciter = 0; - static char *kwlist[] = {"passphrase", "iter", "maciter", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, keywds, "|zii:export", - kwlist, &passphrase, &iter, &maciter)) - return NULL; - - if (self->key != Py_None) { - pkey = ((crypto_PKeyObj*) self->key)->pkey; - } - if (self->cert != Py_None) { - x509 = ((crypto_X509Obj*) self->cert)->x509; - } - if (self->cacerts != Py_None) { - cacerts = sk_X509_new_null(); - for (i = 0; i < PyTuple_Size(self->cacerts); i++) { /* For each CA cert */ - obj = PySequence_GetItem(self->cacerts, i); - /* assert(PyObject_IsInstance(obj, (PyObject *) &crypto_X509_Type )); */ - sk_X509_push(cacerts, (( crypto_X509Obj* ) obj)->x509); - Py_DECREF(obj); - } - } - if (self->friendlyname != Py_None) { - friendly_name = PyBytes_AsString(self->friendlyname); - } - - p12 = PKCS12_create(passphrase, friendly_name, pkey, x509, cacerts, - NID_pbe_WithSHA1And3_Key_TripleDES_CBC, - NID_pbe_WithSHA1And3_Key_TripleDES_CBC, - iter, maciter, 0); - sk_X509_free(cacerts); /* NULL safe. Free just the container. */ - if (p12 == NULL) { - exception_from_error_queue(crypto_Error); - return NULL; - } - bio = BIO_new(BIO_s_mem()); - i2d_PKCS12_bio(bio, p12); - buf_len = BIO_get_mem_data(bio, &temp); - buffer = PyBytes_FromStringAndSize(temp, buf_len); - BIO_free(bio); - return buffer; -} - -/* - * ADD_METHOD(name) expands to a correct PyMethodDef declaration - * { 'name', (PyCFunction)crypto_PKCS12_name, METH_VARARGS, crypto_PKCS12_name_doc } - * for convenience - */ -#define ADD_METHOD(name) \ - { #name, (PyCFunction)crypto_PKCS12_##name, METH_VARARGS, crypto_PKCS12_##name##_doc } -#define ADD_KW_METHOD(name) \ - { #name, (PyCFunction)crypto_PKCS12_##name, METH_VARARGS | METH_KEYWORDS, crypto_PKCS12_##name##_doc } -static PyMethodDef crypto_PKCS12_methods[] = -{ - ADD_METHOD(get_certificate), - ADD_KW_METHOD(set_certificate), - ADD_METHOD(get_privatekey), - ADD_KW_METHOD(set_privatekey), - ADD_METHOD(get_ca_certificates), - ADD_KW_METHOD(set_ca_certificates), - ADD_METHOD(get_friendlyname), - ADD_KW_METHOD(set_friendlyname), - ADD_KW_METHOD(export), - { NULL, NULL } -}; -#undef ADD_METHOD - -/* - * Constructor for PKCS12 objects, never called by Python code directly. - * The strategy for this object is to create all the Python objects - * corresponding to the cert/key/CA certs right away - * - * Arguments: p12 - A "real" PKCS12 object or NULL - * passphrase - Passphrase to use when decrypting the PKCS12 object - * Returns: The newly created PKCS12 object - */ -crypto_PKCS12Obj * -crypto_PKCS12_New(PKCS12 *p12, char *passphrase) { - crypto_PKCS12Obj *self = NULL; - PyObject *cacertobj = NULL; - - unsigned char *alias_str; - int alias_len; - - X509 *cert = NULL; - EVP_PKEY *pkey = NULL; - STACK_OF(X509) *cacerts = NULL; - - int i, cacert_count = 0; - - /* allocate space for the CA cert stack */ - if((cacerts = sk_X509_new_null()) == NULL) { - goto error; /* out of memory? */ - } - - /* parse the PKCS12 lump */ - if (p12) { - if (!PKCS12_parse(p12, passphrase, &pkey, &cert, &cacerts)) { - /* - * If PKCS12_parse fails, and it allocated cacerts, it seems to - * free cacerts, but not re-NULL the pointer. Zounds! Make sure - * it is re-set to NULL here, else we'll have a double-free below. - */ - cacerts = NULL; - exception_from_error_queue(crypto_Error); - goto error; - } else { - /* - * OpenSSL 1.0.0 sometimes leaves an X509_check_private_key error in - * the queue for no particular reason. This error isn't interesting - * to anyone outside this function. It's not even interesting to - * us. Get rid of it. - */ - flush_error_queue(); - } - } - - if (!(self = PyObject_GC_New(crypto_PKCS12Obj, &crypto_PKCS12_Type))) { - goto error; - } - - /* client certificate and friendlyName */ - if (cert == NULL) { - Py_INCREF(Py_None); - self->cert = Py_None; - Py_INCREF(Py_None); - self->friendlyname = Py_None; - } else { - if ((self->cert = (PyObject *)crypto_X509_New(cert, 1)) == NULL) { - goto error; - } - - /* Now we need to extract the friendlyName of the PKCS12 - * that was stored by PKCS_parse() in the alias of the - * certificate. */ - alias_str = X509_alias_get0(cert, &alias_len); - if (alias_str) { - self->friendlyname = Py_BuildValue(BYTESTRING_FMT "#", alias_str, alias_len); - if (!self->friendlyname) { - /* - * XXX Untested - */ - goto error; - } - /* success */ - } else { - Py_INCREF(Py_None); - self->friendlyname = Py_None; - } - } - - /* private key */ - if (pkey == NULL) { - Py_INCREF(Py_None); - self->key = Py_None; - } else { - if ((self->key = (PyObject *)crypto_PKey_New(pkey, 1)) == NULL) - goto error; - } - - /* CA certs */ - cacert_count = sk_X509_num(cacerts); - if (cacert_count <= 0) { - Py_INCREF(Py_None); - self->cacerts = Py_None; - } else { - if ((self->cacerts = PyTuple_New(cacert_count)) == NULL) { - goto error; - } - - for (i = 0; i < cacert_count; i++) { - cert = sk_X509_value(cacerts, i); - if ((cacertobj = (PyObject *)crypto_X509_New(cert, 1)) == NULL) { - goto error; - } - PyTuple_SET_ITEM(self->cacerts, i, cacertobj); - } - } - - sk_X509_free(cacerts); /* Don't free the certs, just the container. */ - PyObject_GC_Track(self); - - return self; - -error: - sk_X509_free(cacerts); /* NULL safe. Free just the container. */ - if (self) { - crypto_PKCS12_clear(self); - PyObject_GC_Del(self); - } - return NULL; -} - -static char crypto_PKCS12_doc[] = "\n\ -PKCS12() -> PKCS12 instance\n\ -\n\ -Create a new empty PKCS12 object.\n\ -\n\ -@returns: The PKCS12 object\n\ -"; -static PyObject * -crypto_PKCS12_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { - if (!PyArg_ParseTuple(args, ":PKCS12")) { - return NULL; - } - - return (PyObject *)crypto_PKCS12_New(NULL, NULL); -} - -/* - * Call the visitproc on all contained objects. - * - * Arguments: self - The PKCS12 object - * visit - Function to call - * arg - Extra argument to visit - * Returns: 0 if all goes well, otherwise the return code from the first - * call that gave non-zero result. - */ -static int -crypto_PKCS12_traverse(crypto_PKCS12Obj *self, visitproc visit, void *arg) -{ - int ret = 0; - - if (ret == 0 && self->cert != NULL) - ret = visit(self->cert, arg); - if (ret == 0 && self->key != NULL) - ret = visit(self->key, arg); - if (ret == 0 && self->cacerts != NULL) - ret = visit(self->cacerts, arg); - if (ret == 0 && self->friendlyname != NULL) - ret = visit(self->friendlyname, arg); - return ret; -} - -/* - * Decref all contained objects and zero the pointers. - * - * Arguments: self - The PKCS12 object - * Returns: Always 0. - */ -static int -crypto_PKCS12_clear(crypto_PKCS12Obj *self) -{ - Py_XDECREF(self->cert); - self->cert = NULL; - Py_XDECREF(self->key); - self->key = NULL; - Py_XDECREF(self->cacerts); - self->cacerts = NULL; - Py_XDECREF(self->friendlyname); - self->friendlyname = NULL; - return 0; -} - -/* - * Deallocate the memory used by the PKCS12 object - * - * Arguments: self - The PKCS12 object - * Returns: None - */ -static void -crypto_PKCS12_dealloc(crypto_PKCS12Obj *self) -{ - PyObject_GC_UnTrack(self); - crypto_PKCS12_clear(self); - PyObject_GC_Del(self); -} - -PyTypeObject crypto_PKCS12_Type = { - PyOpenSSL_HEAD_INIT(&PyType_Type, 0) - "PKCS12", - sizeof(crypto_PKCS12Obj), - 0, - (destructor)crypto_PKCS12_dealloc, - NULL, /* print */ - NULL, /* getattr */ - NULL, /* setattr */ - NULL, /* compare */ - NULL, /* repr */ - NULL, /* as_number */ - NULL, /* as_sequence */ - NULL, /* as_mapping */ - NULL, /* hash */ - NULL, /* call */ - NULL, /* str */ - NULL, /* getattro */ - NULL, /* setattro */ - NULL, /* as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, - crypto_PKCS12_doc, - (traverseproc)crypto_PKCS12_traverse, - (inquiry)crypto_PKCS12_clear, - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - crypto_PKCS12_methods, /* tp_methods */ - NULL, /* tp_members */ - NULL, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - NULL, /* tp_init */ - NULL, /* tp_alloc */ - crypto_PKCS12_new, /* tp_new */ -}; - -/* - * Initialize the PKCS12 part of the crypto sub module - * - * Arguments: module - The crypto module - * Returns: None - */ -int -init_crypto_pkcs12(PyObject *module) { - if (PyType_Ready(&crypto_PKCS12_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_PKCS12_Type); - if (PyModule_AddObject(module, "PKCS12", (PyObject *)&crypto_PKCS12_Type) != 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_PKCS12_Type); - if (PyModule_AddObject(module, "PKCS12Type", (PyObject *)&crypto_PKCS12_Type) != 0) { - return 0; - } - - return 1; -} diff --git a/OpenSSL/crypto/pkcs12.h b/OpenSSL/crypto/pkcs12.h deleted file mode 100644 index f0de1a8..0000000 --- a/OpenSSL/crypto/pkcs12.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * pkcs12.h - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * Export PKCS12 functions and data structure. - * - */ -#ifndef PyOpenSSL_crypto_PKCS12_H_ -#define PyOpenSSL_crypto_PKCS12_H_ - -#include -#include -#include - -extern int init_crypto_pkcs12 (PyObject *); - -extern PyTypeObject crypto_PKCS12_Type; - -#define crypto_PKCS12_Check(v) ((v)->ob_type == &crypto_PKCS12_Type) - -typedef struct { - PyObject_HEAD - /* - * These either refer to a PyObject* of the appropriate type, or Py_None if - * they don't have a value. They aren't set to NULL except during - * finalization. - */ - PyObject *cert; - PyObject *key; - PyObject *cacerts; - PyObject *friendlyname; -} crypto_PKCS12Obj; - -crypto_PKCS12Obj * -crypto_PKCS12_New(PKCS12 *p12, char *passphrase); - -#endif diff --git a/OpenSSL/crypto/pkcs7.c b/OpenSSL/crypto/pkcs7.c deleted file mode 100644 index 1770f7f..0000000 --- a/OpenSSL/crypto/pkcs7.c +++ /dev/null @@ -1,216 +0,0 @@ -/* - * pkcs7.c - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * PKCS7 handling code, mostly thin wrappers around OpenSSL. - * See the file RATIONALE for a short explanation of why this module was written. - * - */ -#include -#define crypto_MODULE -#include "crypto.h" - -static char crypto_PKCS7_type_is_signed_doc[] = "\n\ -Check if this NID_pkcs7_signed object\n\ -\n\ -@return: True if the PKCS7 is of type signed\n\ -"; - -static PyObject * -crypto_PKCS7_type_is_signed(crypto_PKCS7Obj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":type_is_signed")) - return NULL; - - if (PKCS7_type_is_signed(self->pkcs7)) - return PyLong_FromLong(1L); - else - return PyLong_FromLong(0L); -} - -static char crypto_PKCS7_type_is_enveloped_doc[] = "\n\ -Check if this NID_pkcs7_enveloped object\n\ -\n\ -@returns: True if the PKCS7 is of type enveloped\n\ -"; - -static PyObject * -crypto_PKCS7_type_is_enveloped(crypto_PKCS7Obj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":type_is_enveloped")) - return NULL; - - if (PKCS7_type_is_enveloped(self->pkcs7)) - return PyLong_FromLong(1L); - else - return PyLong_FromLong(0L); -} - -static char crypto_PKCS7_type_is_signedAndEnveloped_doc[] = "\n\ -Check if this NID_pkcs7_signedAndEnveloped object\n\ -\n\ -@returns: True if the PKCS7 is of type signedAndEnveloped\n\ -"; - -static PyObject * -crypto_PKCS7_type_is_signedAndEnveloped(crypto_PKCS7Obj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":type_is_signedAndEnveloped")) - return NULL; - - if (PKCS7_type_is_signedAndEnveloped(self->pkcs7)) - return PyLong_FromLong(1L); - else - return PyLong_FromLong(0L); -} - -static char crypto_PKCS7_type_is_data_doc[] = "\n\ -Check if this NID_pkcs7_data object\n\ -\n\ -@return: True if the PKCS7 is of type data\n\ -"; - -static PyObject * -crypto_PKCS7_type_is_data(crypto_PKCS7Obj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":type_is_data")) - return NULL; - - if (PKCS7_type_is_data(self->pkcs7)) - return PyLong_FromLong(1L); - else - return PyLong_FromLong(0L); -} - -static char crypto_PKCS7_get_type_name_doc[] = "\n\ -Returns the type name of the PKCS7 structure\n\ -\n\ -@return: A string with the typename\n\ -"; - -static PyObject * -crypto_PKCS7_get_type_name(crypto_PKCS7Obj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":get_type_name")) - return NULL; - - /* - * return a string with the typename - */ - return PyBytes_FromString(OBJ_nid2sn(OBJ_obj2nid(self->pkcs7->type))); -} - -/* - * ADD_METHOD(name) expands to a correct PyMethodDef declaration - * { 'name', (PyCFunction)crypto_PKCS7_name, METH_VARARGS } - * for convenience - */ -#define ADD_METHOD(name) \ - { #name, (PyCFunction)crypto_PKCS7_##name, METH_VARARGS, crypto_PKCS7_##name##_doc } -static PyMethodDef crypto_PKCS7_methods[] = -{ - ADD_METHOD(type_is_signed), - ADD_METHOD(type_is_enveloped), - ADD_METHOD(type_is_signedAndEnveloped), - ADD_METHOD(type_is_data), - ADD_METHOD(get_type_name), - { NULL, NULL } -}; -#undef ADD_METHOD - - -/* - * Constructor for PKCS7 objects, never called by Python code directly - * - * Arguments: pkcs7 - A "real" pkcs7 certificate object - * dealloc - Boolean value to specify whether the destructor should - * free the "real" pkcs7 object - * Returns: The newly created pkcs7 object - */ -crypto_PKCS7Obj * -crypto_PKCS7_New(PKCS7 *pkcs7, int dealloc) -{ - crypto_PKCS7Obj *self; - - self = PyObject_New(crypto_PKCS7Obj, &crypto_PKCS7_Type); - - if (self == NULL) - return NULL; - - self->pkcs7 = pkcs7; - self->dealloc = dealloc; - - return self; -} - -/* - * Deallocate the memory used by the PKCS7 object - * - * Arguments: self - The PKCS7 object - * Returns: None - */ -static void -crypto_PKCS7_dealloc(crypto_PKCS7Obj *self) -{ - /* Sometimes we don't have to dealloc the "real" PKCS7 pointer ourselves */ - if (self->dealloc) - PKCS7_free(self->pkcs7); - - PyObject_Del(self); -} - -PyTypeObject crypto_PKCS7_Type = { - PyOpenSSL_HEAD_INIT(&PyType_Type, 0) - "PKCS7", - sizeof(crypto_PKCS7Obj), - 0, - (destructor)crypto_PKCS7_dealloc, - NULL, /* print */ - NULL, /* getattr */ - NULL, /* setattr */ - NULL, /* compare */ - NULL, /* repr */ - NULL, /* as_number */ - NULL, /* as_sequence */ - NULL, /* as_mapping */ - NULL, /* hash */ - NULL, /* call */ - NULL, /* str */ - NULL, /* getattro */ - NULL, /* setattro */ - NULL, /* as_buffer */ - Py_TPFLAGS_DEFAULT, - NULL, /* doc */ - NULL, /* traverse */ - NULL, /* clear */ - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - crypto_PKCS7_methods, /* tp_methods */ -}; - -/* - * Initialize the PKCS7 part of the crypto sub module - * - * Arguments: module - The crypto module - * Returns: None - */ -int -init_crypto_pkcs7(PyObject *module) { - if (PyType_Ready(&crypto_PKCS7_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_PKCS7_Type); - if (PyModule_AddObject(module, "PKCS7Type", (PyObject *)&crypto_PKCS7_Type) != 0) { - return 0; - } - - return 1; -} - diff --git a/OpenSSL/crypto/pkcs7.h b/OpenSSL/crypto/pkcs7.h deleted file mode 100644 index d8453b2..0000000 --- a/OpenSSL/crypto/pkcs7.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * pkcs7.h - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * Export pkcs7 functions and data structure. - * See the file RATIONALE for a short explanation of why this module was written. - * - */ -#ifndef PyOpenSSL_crypto_PKCS7_H_ -#define PyOpenSSL_crypto_PKCS7_H_ - -#include -#include - -extern int init_crypto_pkcs7 (PyObject *); - -extern PyTypeObject crypto_PKCS7_Type; - -#define crypto_PKCS7_Check(v) ((v)->ob_type == &crypto_PKCS7_Type) - -typedef struct { - PyObject_HEAD - PKCS7 *pkcs7; - int dealloc; -} crypto_PKCS7Obj; - - -#endif diff --git a/OpenSSL/crypto/pkey.c b/OpenSSL/crypto/pkey.c deleted file mode 100644 index 1f78682..0000000 --- a/OpenSSL/crypto/pkey.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * pkey.c - * - * Copyright (C) AB Strakt - * Copyright (C) Jean-Paul Calderone - * See LICENSE for details. - * - * Public/rivate key handling code, mostly thin wrappers around OpenSSL. - * See the file RATIONALE for a short explanation of why this module was written. - * - */ -#include -#define crypto_MODULE -#include "crypto.h" - -/* - * This is done every time something fails, so turning it into a macro is - * really nice. - * - * Arguments: None - * Returns: Doesn't return - */ -#define FAIL() \ -do { \ - exception_from_error_queue(crypto_Error); \ - return NULL; \ -} while (0) - - -static char crypto_PKey_generate_key_doc[] = "\n\ -Generate a key of a given type, with a given number of a bits\n\ -\n\ -@param type: The key type (TYPE_RSA or TYPE_DSA)\n\ -@param bits: The number of bits\n\ -@return: None\n\ -"; - -static PyObject * -crypto_PKey_generate_key(crypto_PKeyObj *self, PyObject *args) -{ - int type, bits; - RSA *rsa; - DSA *dsa; - - if (!PyArg_ParseTuple(args, "ii:generate_key", &type, &bits)) - return NULL; - - switch (type) - { - case crypto_TYPE_RSA: - if (bits <= 0) { - PyErr_SetString(PyExc_ValueError, "Invalid number of bits"); - return NULL; - } - if ((rsa = RSA_generate_key(bits, 0x10001, NULL, NULL)) == NULL) - FAIL(); - if (!EVP_PKEY_assign_RSA(self->pkey, rsa)) - FAIL(); - break; - - case crypto_TYPE_DSA: - if ((dsa = DSA_generate_parameters(bits, NULL, 0, NULL, NULL, NULL, NULL)) == NULL) - FAIL(); - if (!DSA_generate_key(dsa)) - FAIL(); - if (!EVP_PKEY_assign_DSA(self->pkey, dsa)) - FAIL(); - break; - - default: - PyErr_SetString(crypto_Error, "No such key type"); - return NULL; - - } - self->initialized = 1; - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_PKey_bits_doc[] = "\n\ -Returns the number of bits of the key\n\ -\n\ -@return: The number of bits of the key.\n\ -"; - -static PyObject * -crypto_PKey_bits(crypto_PKeyObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":bits")) - return NULL; - - return PyLong_FromLong(EVP_PKEY_bits(self->pkey)); -} - -static char crypto_PKey_type_doc[] = "\n\ -Returns the type of the key\n\ -\n\ -@return: The type of the key.\n\ -"; - -static PyObject * -crypto_PKey_type(crypto_PKeyObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":type")) - return NULL; - - return PyLong_FromLong(self->pkey->type); -} - -static char crypto_PKey_check_doc[] = "\n\ -Check the consistency of an RSA private key.\n\ -\n\ -@return: True if key is consistent.\n\ -@raise Error: if the key is inconsistent.\n\ -@raise TypeError: if the key is of a type which cannot be checked.\n\ - Only RSA keys can currently be checked.\n\ -"; - -static PyObject * -crypto_PKey_check(crypto_PKeyObj *self, PyObject *args) { - int r; - - if (!PyArg_ParseTuple(args, ":check")) { - return NULL; - } - - if (self->pkey->type == EVP_PKEY_RSA) { - RSA *rsa; - rsa = EVP_PKEY_get1_RSA(self->pkey); - r = RSA_check_key(rsa); - if (r == 1) { - return PyBool_FromLong(1L); - } else { - FAIL(); - } - } else { - PyErr_SetString(PyExc_TypeError, "key type unsupported"); - return NULL; - } -} - -/* - * ADD_METHOD(name) expands to a correct PyMethodDef declaration - * { 'name', (PyCFunction)crypto_PKey_name, METH_VARARGS } - * for convenience - */ -#define ADD_METHOD(name) \ - { #name, (PyCFunction)crypto_PKey_##name, METH_VARARGS, crypto_PKey_##name##_doc } -static PyMethodDef crypto_PKey_methods[] = -{ - ADD_METHOD(generate_key), - ADD_METHOD(bits), - ADD_METHOD(type), - ADD_METHOD(check), - { NULL, NULL } -}; -#undef ADD_METHOD - - -/* - * Constructor for PKey objects, never called by Python code directly - * - * Arguments: pkey - A "real" EVP_PKEY object - * dealloc - Boolean value to specify whether the destructor should - * free the "real" EVP_PKEY object - * Returns: The newly created PKey object - */ -crypto_PKeyObj * -crypto_PKey_New(EVP_PKEY *pkey, int dealloc) -{ - crypto_PKeyObj *self; - - self = PyObject_New(crypto_PKeyObj, &crypto_PKey_Type); - - if (self == NULL) - return NULL; - - self->pkey = pkey; - self->dealloc = dealloc; - self->only_public = 0; - - /* - * Heuristic. Most call-sites pass an initialized EVP_PKEY. Not - * necessarily the case that they will, though. That's part of why this is - * a hack. -exarkun - */ - self->initialized = 1; - - return self; -} - -static char crypto_PKey_doc[] = "\n\ -PKey() -> PKey instance\n\ -\n\ -Create a new PKey object.\n\ -\n\ -@return: The PKey object\n\ -"; -static PyObject* -crypto_PKey_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { - crypto_PKeyObj *self; - - if (!PyArg_ParseTuple(args, ":PKey")) { - return NULL; - } - - self = crypto_PKey_New(EVP_PKEY_new(), 1); - if (self) { - self->initialized = 0; - } - - return (PyObject *)self; -} - - -/* - * Deallocate the memory used by the PKey object - * - * Arguments: self - The PKey object - * Returns: None - */ -static void -crypto_PKey_dealloc(crypto_PKeyObj *self) -{ - /* Sometimes we don't have to dealloc the "real" EVP_PKEY pointer ourselves */ - if (self->dealloc) - EVP_PKEY_free(self->pkey); - - PyObject_Del(self); -} - -PyTypeObject crypto_PKey_Type = { - PyOpenSSL_HEAD_INIT(&PyType_Type, 0) - "OpenSSL.crypto.PKey", - sizeof(crypto_PKeyObj), - 0, - (destructor)crypto_PKey_dealloc, - NULL, /* print */ - NULL, /* getattr */ - NULL, /* setattr */ - NULL, /* compare */ - NULL, /* repr */ - NULL, /* as_number */ - NULL, /* as_sequence */ - NULL, /* as_mapping */ - NULL, /* hash */ - NULL, /* call */ - NULL, /* str */ - NULL, /* getattro */ - NULL, /* setattro */ - NULL, /* as_buffer */ - Py_TPFLAGS_DEFAULT, - crypto_PKey_doc, /* doc */ - NULL, /* traverse */ - NULL, /* clear */ - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - crypto_PKey_methods, /* tp_methods */ - NULL, /* tp_members */ - NULL, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - NULL, /* tp_init */ - NULL, /* tp_alloc */ - crypto_PKey_new, /* tp_new */ -}; - - -/* - * Initialize the PKey part of the crypto sub module - * - * Arguments: module - The crypto module - * Returns: None - */ -int -init_crypto_pkey(PyObject *module) -{ - if (PyType_Ready(&crypto_PKey_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_PKey_Type); - if (PyModule_AddObject(module, "PKey", (PyObject *)&crypto_PKey_Type) != 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_PKey_Type); - if (PyModule_AddObject(module, "PKeyType", (PyObject *)&crypto_PKey_Type) != 0) { - return 0; - } - - return 1; -} - diff --git a/OpenSSL/crypto/pkey.h b/OpenSSL/crypto/pkey.h deleted file mode 100644 index dc5e52e..0000000 --- a/OpenSSL/crypto/pkey.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * pkey.h - * - * Copyright (C) AB Strakt - * Copyright (C) Jean-Paul Calderone - * See LICENSE for details. - * - * Export pkey functions and data structure. - * See the file RATIONALE for a short explanation of why this module was written. - * - */ -#ifndef PyOpenSSL_crypto_PKEY_H_ -#define PyOpenSSL_crypto_PKEY_H_ - -extern int init_crypto_pkey (PyObject *); - -extern PyTypeObject crypto_PKey_Type; - -#define crypto_PKey_Check(v) ((v)->ob_type == &crypto_PKey_Type) - -typedef struct { - PyObject_HEAD - - /* - * A pointer to the underlying OpenSSL structure. - */ - EVP_PKEY *pkey; - - /* - * A flag indicating the underlying pkey object has no private parts (so it - * can't sign, for example). This is a bit of a temporary hack. - * Public-only should be represented as a different type. -exarkun - */ - int only_public; - - /* - * A flag indicating whether the underlying pkey object has no meaningful - * data in it whatsoever. This is a temporary hack. It should be - * impossible to create PKeys in an unusable state. -exarkun - */ - int initialized; - - /* - * A flag indicating whether pkey will be freed when this object is freed. - */ - int dealloc; -} crypto_PKeyObj; - -#define crypto_TYPE_RSA EVP_PKEY_RSA -#define crypto_TYPE_DSA EVP_PKEY_DSA - -#endif diff --git a/OpenSSL/crypto/revoked.c b/OpenSSL/crypto/revoked.c deleted file mode 100644 index 93f9946..0000000 --- a/OpenSSL/crypto/revoked.c +++ /dev/null @@ -1,444 +0,0 @@ -#include -#define crypto_MODULE -#include "crypto.h" - -#ifdef _WIN32 -#define strcasecmp(string1, string2) _stricmp(string1, string2) -#endif - -/* http://www.openssl.org/docs/apps/x509v3_config.html#CRL_distribution_points_ */ -/* which differs from crl_reasons of crypto/x509v3/v3_enum.c that matches */ -/* OCSP_crl_reason_str. We use the latter, just like the command line program. */ -static const char *crl_reasons[] = { - "unspecified", - "keyCompromise", - "CACompromise", - "affiliationChanged", - "superseded", - "cessationOfOperation", - "certificateHold", - NULL, - "removeFromCRL", -}; - -#define NUM_REASONS (sizeof(crl_reasons) / sizeof(char *)) - -static char crypto_Revoked_all_reasons_doc[] = "\n\ -Return a list of all the supported reason strings.\n\ -\n\ -@return: A list of reason strings.\n\ -"; -static PyObject * -crypto_Revoked_all_reasons(crypto_RevokedObj *self, PyObject *args) { - PyObject *list, *str; - int j; - - list = PyList_New(0); - for (j = 0; j < NUM_REASONS; j++) { - if(crl_reasons[j]) { - str = PyBytes_FromString(crl_reasons[j]); - PyList_Append(list, str); - Py_DECREF(str); - } - } - return list; -} - -static PyObject * -X509_EXTENSION_value_to_PyString(X509_EXTENSION *ex) { - BIO *bio = NULL; - PyObject *str = NULL; - int str_len; - char *tmp_str; - - /* Create a openssl BIO buffer */ - bio = BIO_new(BIO_s_mem()); - if (bio == NULL) { - goto err; - } - - /* These are not the droids you are looking for. */ - if (!X509V3_EXT_print(bio, ex, 0, 0)) { - if (M_ASN1_OCTET_STRING_print(bio, ex->value) == 0) { - goto err; - } - } - - /* Convert to a Python string. */ - str_len = BIO_get_mem_data(bio, &tmp_str); - str = PyBytes_FromStringAndSize(tmp_str, str_len); - - /* Cleanup */ - BIO_free(bio); - return str; - - err: - if (bio) { - BIO_free(bio); - } - if (str) { - Py_DECREF(str); - } - return NULL; -} - -static void -delete_reason(STACK_OF(X509_EXTENSION) *sk) { - X509_EXTENSION * ext; - int j; - - for (j = 0; j < sk_X509_EXTENSION_num(sk); j++) { - ext = sk_X509_EXTENSION_value(sk, j); - if (OBJ_obj2nid(ext->object) == NID_crl_reason) { - X509_EXTENSION_free(ext); - (void) sk_X509_EXTENSION_delete(sk, j); - break; - } - } -} - -static int -reason_str_to_code(const char * reason_str) { - int reason_code = -1, j; - char *spaceless_reason, * sp; - - /* Remove spaces so that the responses of - * get_reason() work in set_reason() */ - if ((spaceless_reason = strdup(reason_str)) == NULL) { - return -1; - } - - while ((sp = strchr(spaceless_reason, ' '))) { - memmove(sp, sp+1, strlen(sp)); - } - - for (j = 0; j < NUM_REASONS; j++) { - if(crl_reasons[j] && !strcasecmp(spaceless_reason, crl_reasons[j])) { - reason_code = j; - break; - } - } - free(spaceless_reason); - return reason_code; -} - - -static char crypto_Revoked_set_reason_doc[] = "\n\ -Set the reason of a Revoked object.\n\ -\n\ -@param reason: The reason string.\n\ -@type reason: L{str}\n\ -@return: None\n\ -"; -static PyObject * -crypto_Revoked_set_reason(crypto_RevokedObj *self, PyObject *args, PyObject *keywds) { - static char *kwlist[] = {"reason", NULL}; - const char *reason_str = NULL; - int reason_code; - ASN1_ENUMERATED *rtmp = NULL; - - if (!PyArg_ParseTupleAndKeywords( - args, keywds, "O&:set_reason", kwlist, - crypto_byte_converter, &reason_str)) { - return NULL; - } - - if(reason_str == NULL) { - delete_reason(self->revoked->extensions); - goto done; - } - - reason_code = reason_str_to_code(reason_str); - if (reason_code == -1) { - PyErr_SetString(PyExc_ValueError, "bad reason string"); - return NULL; - } - - rtmp = ASN1_ENUMERATED_new(); - if (!rtmp || !ASN1_ENUMERATED_set(rtmp, reason_code)) { - goto err; - } - delete_reason(self->revoked->extensions); - if (!X509_REVOKED_add1_ext_i2d(self->revoked, NID_crl_reason, rtmp, 0, 0)) { - goto err; - } - - done: - Py_INCREF(Py_None); - return Py_None; - - err: - exception_from_error_queue(crypto_Error); - return NULL; -} - - -static char crypto_Revoked_get_reason_doc[] = "\n\ -Return the reason of a Revoked object.\n\ -\n\ -@return: The reason as a string\n\ -"; -static PyObject * -crypto_Revoked_get_reason(crypto_RevokedObj *self, PyObject *args) { - X509_EXTENSION * ext; - int j; - STACK_OF(X509_EXTENSION) *sk = NULL; - - if (!PyArg_ParseTuple(args, ":get_reason")) { - return NULL; - } - - sk = self->revoked->extensions; - for (j = 0; j < sk_X509_EXTENSION_num(sk); j++) { - ext = sk_X509_EXTENSION_value(sk, j); - if (OBJ_obj2nid(ext->object) == NID_crl_reason) { - return X509_EXTENSION_value_to_PyString(ext); - } - } - - Py_INCREF(Py_None); - return Py_None; -} - - -static char crypto_Revoked_get_rev_date_doc[] = "\n\ -Retrieve the revocation date\n\ -\n\ -@return: A string giving the timestamp, in the format:\n\ -\n\ - YYYYMMDDhhmmssZ\n\ - YYYYMMDDhhmmss+hhmm\n\ - YYYYMMDDhhmmss-hhmm\n\ -"; - -static PyObject* -crypto_Revoked_get_rev_date(crypto_RevokedObj *self, PyObject *args) { - /* returns a borrowed reference. */ - return _get_asn1_time( - ":get_rev_date", self->revoked->revocationDate, args); -} - -static char crypto_Revoked_set_rev_date_doc[] = "\n\ -Set the revocation timestamp\n\ -\n\ -@param when: A string giving the timestamp, in the format:\n\ -\n\ - YYYYMMDDhhmmssZ\n\ - YYYYMMDDhhmmss+hhmm\n\ - YYYYMMDDhhmmss-hhmm\n\ -\n\ -@return: None\n\ -"; - -static PyObject* -crypto_Revoked_set_rev_date(crypto_RevokedObj *self, PyObject *args) { - return _set_asn1_time( - BYTESTRING_FMT ":set_rev_date", self->revoked->revocationDate, args); -} - -/* The integer is converted to an upper-case hex string - * without a '0x' prefix. */ -static PyObject * -ASN1_INTEGER_to_PyString(ASN1_INTEGER *asn1_int) { - BIO *bio = NULL; - PyObject *str = NULL; - int str_len; - char *tmp_str; - - /* Create a openssl BIO buffer */ - bio = BIO_new(BIO_s_mem()); - if (bio == NULL) { - goto err; - } - - /* Write the integer to the BIO as a hex string. */ - if (i2a_ASN1_INTEGER(bio, asn1_int) < 0) { - goto err; - } - - /* Convert to a Python string. */ - str_len = BIO_get_mem_data(bio, &tmp_str); - str = PyBytes_FromStringAndSize(tmp_str, str_len); - - /* Cleanup */ - BIO_free(bio); - return str; - - err: - if (bio) { - BIO_free(bio); - } - if (str) { - Py_DECREF(str); - } - return NULL; -} - - -static char crypto_Revoked_get_serial_doc[] = "\n\ -Return the serial number of a Revoked structure\n\ -\n\ -@return: The serial number as a string\n\ -"; -static PyObject * -crypto_Revoked_get_serial(crypto_RevokedObj *self, PyObject *args) { - if (!PyArg_ParseTuple(args, ":get_serial")) { - return NULL; - } - - if (self->revoked->serialNumber == NULL) { - /* never happens */ - Py_INCREF(Py_None); - return Py_None; - } else { - return ASN1_INTEGER_to_PyString(self->revoked->serialNumber); - } -} - -static char crypto_Revoked_set_serial_doc[] = "\n\ -Set the serial number of a revoked Revoked structure\n\ -\n\ -@param hex_str: The new serial number.\n\ -@type hex_str: L{str}\n\ -@return: None\n\ -"; -static PyObject * -crypto_Revoked_set_serial(crypto_RevokedObj *self, PyObject *args, PyObject *keywds) { - static char *kwlist[] = {"hex_str", NULL}; - const char *hex_str = NULL; - BIGNUM *serial = NULL; - ASN1_INTEGER *tmpser = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, keywds, BYTESTRING_FMT ":set_serial", - kwlist, &hex_str)) { - return NULL; - } - - if (!BN_hex2bn(&serial, hex_str) ) { - PyErr_SetString(PyExc_ValueError, "bad hex string"); - return NULL; - } - - tmpser = BN_to_ASN1_INTEGER(serial, NULL); - BN_free(serial); - serial = NULL; - X509_REVOKED_set_serialNumber(self->revoked, tmpser); - ASN1_INTEGER_free(tmpser); - - Py_INCREF(Py_None); - return Py_None; -} - - -crypto_RevokedObj * -crypto_Revoked_New(X509_REVOKED *revoked) { - crypto_RevokedObj *self; - - self = PyObject_New(crypto_RevokedObj, &crypto_Revoked_Type); - if (self == NULL) { - return NULL; - } - self->revoked = revoked; - return self; -} - -/* - * ADD_METHOD(name) expands to a correct PyMethodDef declaration - * { 'name', (PyCFunction)crypto_Revoked_name, METH_VARARGS, crypto_Revoked_name_doc } - * for convenience - */ -#define ADD_METHOD(name) \ - { #name, (PyCFunction)crypto_Revoked_##name, METH_VARARGS, crypto_Revoked_##name##_doc } -#define ADD_KW_METHOD(name) \ - { #name, (PyCFunction)crypto_Revoked_##name, METH_VARARGS | METH_KEYWORDS, crypto_Revoked_##name##_doc } -static PyMethodDef crypto_Revoked_methods[] = { - ADD_METHOD(all_reasons), - ADD_METHOD(get_reason), - ADD_KW_METHOD(set_reason), - ADD_METHOD(get_rev_date), - ADD_METHOD(set_rev_date), - ADD_METHOD(get_serial), - ADD_KW_METHOD(set_serial), - { NULL, NULL } -}; -#undef ADD_METHOD - - -static void -crypto_Revoked_dealloc(crypto_RevokedObj *self) { - X509_REVOKED_free(self->revoked); - self->revoked = NULL; - - PyObject_Del(self); -} - -static char crypto_Revoked_doc[] = "\n\ -Revoked() -> Revoked instance\n\ -\n\ -Create a new empty Revoked object.\n\ -\n\ -@returns: The Revoked object\n\ -"; - -static PyObject* crypto_Revoked_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { - if (!PyArg_ParseTuple(args, ":Revoked")) { - return NULL; - } - - return (PyObject *)crypto_Revoked_New(X509_REVOKED_new()); -} - -PyTypeObject crypto_Revoked_Type = { - PyOpenSSL_HEAD_INIT(&PyType_Type, 0) - "Revoked", - sizeof(crypto_RevokedObj), - 0, - (destructor)crypto_Revoked_dealloc, - NULL, /* print */ - NULL, /* getattr */ - NULL, /* setattr */ - NULL, /* compare */ - NULL, /* repr */ - NULL, /* as_number */ - NULL, /* as_sequence */ - NULL, /* as_mapping */ - NULL, /* hash */ - NULL, /* call */ - NULL, /* str */ - NULL, /* getattro */ - NULL, /* setattro */ - NULL, /* as_buffer */ - Py_TPFLAGS_DEFAULT, - crypto_Revoked_doc, /* doc */ - NULL, /* traverse */ - NULL, /* clear */ - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - crypto_Revoked_methods, /* tp_methods */ - NULL, /* tp_members */ - NULL, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - NULL, /* tp_init */ - NULL, /* tp_alloc */ - crypto_Revoked_new, /* tp_new */ -}; - -int init_crypto_revoked(PyObject *module) { - if(PyType_Ready(&crypto_Revoked_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_Revoked_Type); - if (PyModule_AddObject(module, "Revoked", (PyObject *)&crypto_Revoked_Type) != 0) { - return 0; - } - return 1; -} diff --git a/OpenSSL/crypto/revoked.h b/OpenSSL/crypto/revoked.h deleted file mode 100644 index fb85ac6..0000000 --- a/OpenSSL/crypto/revoked.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef PyOpenSSL_crypto_REVOKED_H_ -#define PyOpenSSL_crypto_REVOKED_H_ - -#include - -extern PyTypeObject crypto_Revoked_Type; - -#define crypto_Revoked_Check(v) ((v)->ob_type == &crypto_Revoked_Type) - -typedef struct { - PyObject_HEAD - X509_REVOKED *revoked; -} crypto_RevokedObj; - -extern int init_crypto_revoked (PyObject *); -extern crypto_RevokedObj * crypto_Revoked_New(X509_REVOKED *revoked); - -#endif diff --git a/OpenSSL/crypto/x509.c b/OpenSSL/crypto/x509.c deleted file mode 100644 index 0754dec..0000000 --- a/OpenSSL/crypto/x509.c +++ /dev/null @@ -1,929 +0,0 @@ -/* - * x509.c - * - * Copyright (C) AB Strakt - * Copyright (C) Jean-Paul Calderone - * See LICENSE for details. - * - * Certificate (X.509) handling code, mostly thin wrappers around OpenSSL. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - */ -#include -#define crypto_MODULE -#include "crypto.h" -#include "x509ext.h" - -/* - * X.509 is a standard for digital certificates. See e.g. the OpenSSL homepage - * http://www.openssl.org/ for more information - */ - -static char crypto_X509_get_version_doc[] = "\n\ -Return version number of the certificate\n\ -\n\ -@return: Version number as a Python integer\n\ -"; - -static PyObject * -crypto_X509_get_version(crypto_X509Obj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":get_version")) - return NULL; - - return PyLong_FromLong((long)X509_get_version(self->x509)); -} - -static char crypto_X509_set_version_doc[] = "\n\ -Set version number of the certificate\n\ -\n\ -@param version: The version number\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509_set_version(crypto_X509Obj *self, PyObject *args) -{ - int version; - - if (!PyArg_ParseTuple(args, "i:set_version", &version)) - return NULL; - - X509_set_version(self->x509, version); - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_X509_get_serial_number_doc[] = "\n\ -Return serial number of the certificate\n\ -\n\ -@return: Serial number as a Python integer\n\ -"; - -static PyObject * -crypto_X509_get_serial_number(crypto_X509Obj *self, PyObject *args) -{ - ASN1_INTEGER *asn1_i; - BIGNUM *bignum; - char *hex; - PyObject *res; - - if (!PyArg_ParseTuple(args, ":get_serial_number")) - return NULL; - - asn1_i = X509_get_serialNumber(self->x509); - bignum = ASN1_INTEGER_to_BN(asn1_i, NULL); - hex = BN_bn2hex(bignum); - res = PyLong_FromString(hex, NULL, 16); - BN_free(bignum); - free(hex); - return res; -} - -static char crypto_X509_set_serial_number_doc[] = "\n\ -Set serial number of the certificate\n\ -\n\ -@param serial: The serial number\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509_set_serial_number(crypto_X509Obj *self, PyObject *args) -{ - long small_serial; - PyObject *serial = NULL; - PyObject *hex = NULL; - ASN1_INTEGER *asn1_i = NULL; - BIGNUM *bignum = NULL; - char *hexstr; - - if (!PyArg_ParseTuple(args, "O:set_serial_number", &serial)) { - return NULL; - } - - if (!PyOpenSSL_Integer_Check(serial)) { - PyErr_SetString( - PyExc_TypeError, "serial number must be integer"); - goto err; - } - - if ((hex = PyOpenSSL_LongToHex(serial)) == NULL) { - goto err; - } - -#ifdef PY3 - { - PyObject *hexbytes = PyUnicode_AsASCIIString(hex); - Py_DECREF(hex); - hex = hexbytes; - } -#endif - - /** - * BN_hex2bn stores the result in &bignum. Unless it doesn't feel like - * it. If bignum is still NULL after this call, then the return value - * is actually the result. I hope. -exarkun - */ - hexstr = PyBytes_AsString(hex); - if (hexstr[1] == 'x') { - /* +2 to skip the "0x" */ - hexstr += 2; - } - small_serial = BN_hex2bn(&bignum, hexstr); - - Py_DECREF(hex); - hex = NULL; - - if (bignum == NULL) { - if (ASN1_INTEGER_set(X509_get_serialNumber(self->x509), small_serial)) { - exception_from_error_queue(crypto_Error); - goto err; - } - } else { - asn1_i = BN_to_ASN1_INTEGER(bignum, NULL); - BN_free(bignum); - bignum = NULL; - if (asn1_i == NULL) { - exception_from_error_queue(crypto_Error); - goto err; - } - if (!X509_set_serialNumber(self->x509, asn1_i)) { - exception_from_error_queue(crypto_Error); - goto err; - } - ASN1_INTEGER_free(asn1_i); - asn1_i = NULL; - } - - Py_INCREF(Py_None); - return Py_None; - - err: - if (hex) { - Py_DECREF(hex); - } - if (bignum) { - BN_free(bignum); - } - if (asn1_i) { - ASN1_INTEGER_free(asn1_i); - } - return NULL; -} - -static char crypto_X509_get_issuer_doc[] = "\n\ -Create an X509Name object for the issuer of the certificate\n\ -\n\ -@return: An X509Name object\n\ -"; - -static PyObject * -crypto_X509_get_issuer(crypto_X509Obj *self, PyObject *args) -{ - crypto_X509NameObj *pyname; - X509_NAME *name; - - if (!PyArg_ParseTuple(args, ":get_issuer")) - return NULL; - - name = X509_get_issuer_name(self->x509); - pyname = crypto_X509Name_New(name, 0); - if (pyname != NULL) - { - pyname->parent_cert = (PyObject *)self; - Py_INCREF(self); - } - return (PyObject *)pyname; -} - -static char crypto_X509_set_issuer_doc[] = "\n\ -Set the issuer of the certificate\n\ -\n\ -@param issuer: The issuer name\n\ -@type issuer: L{X509Name}\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509_set_issuer(crypto_X509Obj *self, PyObject *args) -{ - crypto_X509NameObj *issuer; - - if (!PyArg_ParseTuple(args, "O!:set_issuer", &crypto_X509Name_Type, - &issuer)) - return NULL; - - if (!X509_set_issuer_name(self->x509, issuer->x509_name)) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_X509_get_subject_doc[] = "\n\ -Create an X509Name object for the subject of the certificate\n\ -\n\ -@return: An X509Name object\n\ -"; - -static PyObject * -crypto_X509_get_subject(crypto_X509Obj *self, PyObject *args) -{ - crypto_X509NameObj *pyname; - X509_NAME *name; - - if (!PyArg_ParseTuple(args, ":get_subject")) - return NULL; - - name = X509_get_subject_name(self->x509); - pyname = crypto_X509Name_New(name, 0); - if (pyname != NULL) - { - pyname->parent_cert = (PyObject *)self; - Py_INCREF(self); - } - return (PyObject *)pyname; -} - -static char crypto_X509_set_subject_doc[] = "\n\ -Set the subject of the certificate\n\ -\n\ -@param subject: The subject name\n\ -@type subject: L{X509Name}\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509_set_subject(crypto_X509Obj *self, PyObject *args) -{ - crypto_X509NameObj *subject; - - if (!PyArg_ParseTuple(args, "O!:set_subject", &crypto_X509Name_Type, - &subject)) - return NULL; - - if (!X509_set_subject_name(self->x509, subject->x509_name)) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_X509_get_pubkey_doc[] = "\n\ -Get the public key of the certificate\n\ -\n\ -@return: The public key\n\ -"; - -static PyObject * -crypto_X509_get_pubkey(crypto_X509Obj *self, PyObject *args) -{ - crypto_PKeyObj *crypto_PKey_New(EVP_PKEY *, int); - EVP_PKEY *pkey; - crypto_PKeyObj *py_pkey; - - if (!PyArg_ParseTuple(args, ":get_pubkey")) - return NULL; - - if ((pkey = X509_get_pubkey(self->x509)) == NULL) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - py_pkey = crypto_PKey_New(pkey, 1); - if (py_pkey != NULL) { - py_pkey->only_public = 1; - } - return (PyObject *)py_pkey; -} - -static char crypto_X509_set_pubkey_doc[] = "\n\ -Set the public key of the certificate\n\ -\n\ -@param pkey: The public key\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509_set_pubkey(crypto_X509Obj *self, PyObject *args) -{ - crypto_PKeyObj *pkey; - - if (!PyArg_ParseTuple(args, "O!:set_pubkey", &crypto_PKey_Type, &pkey)) - return NULL; - - if (!X509_set_pubkey(self->x509, pkey->pkey)) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - Py_INCREF(Py_None); - return Py_None; -} - -PyObject* -_set_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args) -{ - char *when; - - if (!PyArg_ParseTuple(args, format, &when)) - return NULL; - - if (ASN1_GENERALIZEDTIME_set_string(timestamp, when) == 0) { - ASN1_GENERALIZEDTIME dummy; - dummy.type = V_ASN1_GENERALIZEDTIME; - dummy.length = strlen(when); - dummy.data = (unsigned char *)when; - if (!ASN1_GENERALIZEDTIME_check(&dummy)) { - PyErr_SetString(PyExc_ValueError, "Invalid string"); - } else { - PyErr_SetString(PyExc_RuntimeError, "Unknown ASN1_GENERALIZEDTIME_set_string failure"); - } - return NULL; - } - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_X509_set_notBefore_doc[] = "\n\ -Set the time stamp for when the certificate starts being valid\n\ -\n\ -@param when: A string giving the timestamp, in the format:\n\ -\n\ - YYYYMMDDhhmmssZ\n\ - YYYYMMDDhhmmss+hhmm\n\ - YYYYMMDDhhmmss-hhmm\n\ -\n\ -@return: None\n\ -"; - -static PyObject* -crypto_X509_set_notBefore(crypto_X509Obj *self, PyObject *args) -{ - return _set_asn1_time( - BYTESTRING_FMT ":set_notBefore", - X509_get_notBefore(self->x509), args); -} - -static char crypto_X509_set_notAfter_doc[] = "\n\ -Set the time stamp for when the certificate stops being valid\n\ -\n\ -@param when: A string giving the timestamp, in the format:\n\ -\n\ - YYYYMMDDhhmmssZ\n\ - YYYYMMDDhhmmss+hhmm\n\ - YYYYMMDDhhmmss-hhmm\n\ -\n\ -@return: None\n\ -"; - -static PyObject* -crypto_X509_set_notAfter(crypto_X509Obj *self, PyObject *args) -{ - return _set_asn1_time( - BYTESTRING_FMT ":set_notAfter", - X509_get_notAfter(self->x509), args); -} - -PyObject* -_get_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args) -{ - ASN1_GENERALIZEDTIME *gt_timestamp = NULL; - PyObject *py_timestamp = NULL; - - if (!PyArg_ParseTuple(args, format)) { - return NULL; - } - - /* - * http://www.columbia.edu/~ariel/ssleay/asn1-time.html - */ - /* - * There must be a way to do this without touching timestamp->data - * directly. -exarkun - */ - if (timestamp->length == 0) { - Py_INCREF(Py_None); - return Py_None; - } else if (timestamp->type == V_ASN1_GENERALIZEDTIME) { - return PyBytes_FromString((char *)timestamp->data); - } else { - ASN1_TIME_to_generalizedtime(timestamp, >_timestamp); - if (gt_timestamp == NULL) { - exception_from_error_queue(crypto_Error); - return NULL; - } else { - py_timestamp = PyBytes_FromString((char *)gt_timestamp->data); - ASN1_GENERALIZEDTIME_free(gt_timestamp); - return py_timestamp; - } - } -} - -static char crypto_X509_get_notBefore_doc[] = "\n\ -Retrieve the time stamp for when the certificate starts being valid\n\ -\n\ -@return: A string giving the timestamp, in the format:\n\ -\n\ - YYYYMMDDhhmmssZ\n\ - YYYYMMDDhhmmss+hhmm\n\ - YYYYMMDDhhmmss-hhmm\n\ - or None if there is no value set.\n\ -"; - -static PyObject* -crypto_X509_get_notBefore(crypto_X509Obj *self, PyObject *args) -{ - /* - * X509_get_notBefore returns a borrowed reference. - */ - return _get_asn1_time( - ":get_notBefore", X509_get_notBefore(self->x509), args); -} - - -static char crypto_X509_get_notAfter_doc[] = "\n\ -Retrieve the time stamp for when the certificate stops being valid\n\ -\n\ -@return: A string giving the timestamp, in the format:\n\ -\n\ - YYYYMMDDhhmmssZ\n\ - YYYYMMDDhhmmss+hhmm\n\ - YYYYMMDDhhmmss-hhmm\n\ - or None if there is no value set.\n\ -"; - -static PyObject* -crypto_X509_get_notAfter(crypto_X509Obj *self, PyObject *args) -{ - /* - * X509_get_notAfter returns a borrowed reference. - */ - return _get_asn1_time( - ":get_notAfter", X509_get_notAfter(self->x509), args); -} - - -static char crypto_X509_gmtime_adj_notBefore_doc[] = "\n\ -Change the timestamp for when the certificate starts being valid to the current\n\ -time plus an offset.\n \ -\n\ -@param amount: The number of seconds by which to adjust the starting validity\n\ - time.\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509_gmtime_adj_notBefore(crypto_X509Obj *self, PyObject *args) -{ - long amount; - - if (!PyArg_ParseTuple(args, "l:gmtime_adj_notBefore", &amount)) - return NULL; - - X509_gmtime_adj(X509_get_notBefore(self->x509), amount); - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_X509_gmtime_adj_notAfter_doc[] = "\n\ -Adjust the time stamp for when the certificate stops being valid\n\ -\n\ -@param amount: The number of seconds by which to adjust the ending validity\n\ - time.\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509_gmtime_adj_notAfter(crypto_X509Obj *self, PyObject *args) -{ - long amount; - - if (!PyArg_ParseTuple(args, "l:gmtime_adj_notAfter", &amount)) - return NULL; - - X509_gmtime_adj(X509_get_notAfter(self->x509), amount); - - Py_INCREF(Py_None); - return Py_None; -} - - -static char crypto_X509_get_signature_algorithm_doc[] = "\n\ -Retrieve the signature algorithm used in the certificate\n\ -\n\ -@return: A byte string giving the name of the signature algorithm used in\n\ - the certificate.\n\ -@raise ValueError: If the signature algorithm is undefined.\n\ -"; - -static PyObject * -crypto_X509_get_signature_algorithm(crypto_X509Obj *self, PyObject *args) { - ASN1_OBJECT *alg; - int nid; - - if (!PyArg_ParseTuple(args, ":get_signature_algorithm")) { - return NULL; - } - - alg = self->x509->cert_info->signature->algorithm; - nid = OBJ_obj2nid(alg); - if (nid == NID_undef) { - PyErr_SetString(PyExc_ValueError, "Undefined signature algorithm"); - return NULL; - } - return PyBytes_FromString(OBJ_nid2ln(nid)); -} - - -static char crypto_X509_sign_doc[] = "\n\ -Sign the certificate using the supplied key and digest\n\ -\n\ -@param pkey: The key to sign with\n\ -@param digest: The message digest to use\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509_sign(crypto_X509Obj *self, PyObject *args) -{ - crypto_PKeyObj *pkey; - char *digest_name; - const EVP_MD *digest; - - if (!PyArg_ParseTuple(args, "O!s:sign", &crypto_PKey_Type, &pkey, - &digest_name)) - return NULL; - - if (pkey->only_public) { - PyErr_SetString(PyExc_ValueError, "Key has only public part"); - return NULL; - } - - if (!pkey->initialized) { - PyErr_SetString(PyExc_ValueError, "Key is uninitialized"); - return NULL; - } - - if ((digest = EVP_get_digestbyname(digest_name)) == NULL) - { - PyErr_SetString(PyExc_ValueError, "No such digest method"); - return NULL; - } - - if (!X509_sign(self->x509, pkey->pkey, digest)) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_X509_has_expired_doc[] = "\n\ -Check whether the certificate has expired.\n\ -\n\ -@return: True if the certificate has expired, false otherwise\n\ -"; - -static PyObject * -crypto_X509_has_expired(crypto_X509Obj *self, PyObject *args) -{ - time_t tnow; - - if (!PyArg_ParseTuple(args, ":has_expired")) - return NULL; - - tnow = time(NULL); - if (ASN1_UTCTIME_cmp_time_t(X509_get_notAfter(self->x509), tnow) < 0) - return PyLong_FromLong(1L); - else - return PyLong_FromLong(0L); -} - -static char crypto_X509_subject_name_hash_doc[] = "\n\ -Return the hash of the X509 subject.\n\ -\n\ -@return: The hash of the subject\n\ -"; - -static PyObject * -crypto_X509_subject_name_hash(crypto_X509Obj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":subject_name_hash")) - return NULL; - - return PyLong_FromLongLong(X509_subject_name_hash(self->x509)); -} - -static char crypto_X509_digest_doc[] = "\n\ -Return the digest of the X509 object.\n\ -\n\ -@return: The digest of the object\n\ -"; - -static PyObject * -crypto_X509_digest(crypto_X509Obj *self, PyObject *args) -{ - unsigned char fp[EVP_MAX_MD_SIZE]; - char *tmp; - char *digest_name; - unsigned int len,i; - PyObject *ret; - const EVP_MD *digest; - - if (!PyArg_ParseTuple(args, "s:digest", &digest_name)) - return NULL; - - if ((digest = EVP_get_digestbyname(digest_name)) == NULL) - { - PyErr_SetString(PyExc_ValueError, "No such digest method"); - return NULL; - } - - if (!X509_digest(self->x509,digest,fp,&len)) - { - exception_from_error_queue(crypto_Error); - } - tmp = malloc(3*len+1); - memset(tmp, 0, 3*len+1); - for (i = 0; i < len; i++) { - sprintf(tmp+i*3,"%02X:",fp[i]); - } - tmp[3*len-1] = 0; - ret = PyBytes_FromStringAndSize(tmp,3*len-1); - free(tmp); - return ret; -} - - -static char crypto_X509_add_extensions_doc[] = "\n\ -Add extensions to the certificate.\n\ -\n\ -@param extensions: a sequence of X509Extension objects\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509_add_extensions(crypto_X509Obj *self, PyObject *args) -{ - PyObject *extensions, *seq; - crypto_X509ExtensionObj *ext; - int nr_of_extensions, i; - - if (!PyArg_ParseTuple(args, "O:add_extensions", &extensions)) - return NULL; - - seq = PySequence_Fast(extensions, "Expected a sequence"); - if (seq == NULL) - return NULL; - - nr_of_extensions = PySequence_Fast_GET_SIZE(seq); - - for (i = 0; i < nr_of_extensions; i++) - { - ext = (crypto_X509ExtensionObj *)PySequence_Fast_GET_ITEM(seq, i); - if (!crypto_X509Extension_Check(ext)) - { - Py_DECREF(seq); - PyErr_SetString(PyExc_ValueError, - "One of the elements is not an X509Extension"); - return NULL; - } - if (!X509_add_ext(self->x509, ext->x509_extension, -1)) - { - Py_DECREF(seq); - exception_from_error_queue(crypto_Error); - return NULL; - } - } - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_X509_get_extension_count_doc[] = "\n\ -Get the number of extensions on the certificate.\n\ -\n\ -@return: Number of extensions as a Python integer\n\ -"; - -static PyObject * -crypto_X509_get_extension_count(crypto_X509Obj *self, PyObject *args) { - if (!PyArg_ParseTuple(args, ":get_extension_count")) { - return NULL; - } - - return PyLong_FromLong((long)X509_get_ext_count(self->x509)); -} - -static char crypto_X509_get_extension_doc[] = "\n\ -Get a specific extension of the certificate by index.\n\ -\n\ -@param index: The index of the extension to retrieve.\n\ -@return: The X509Extension object at the specified index.\n\ -"; - -static PyObject * -crypto_X509_get_extension(crypto_X509Obj *self, PyObject *args) { - crypto_X509ExtensionObj *extobj; - int loc; - X509_EXTENSION *ext; - - if (!PyArg_ParseTuple(args, "i:get_extension", &loc)) { - return NULL; - } - - /* will return NULL if loc is outside the range of extensions, - not registered as an error*/ - ext = X509_get_ext(self->x509, loc); - if (!ext) { - PyErr_SetString(PyExc_IndexError, "extension index out of bounds"); - return NULL; /* Should be reported as an IndexError ? */ - } - - extobj = PyObject_New(crypto_X509ExtensionObj, &crypto_X509Extension_Type); - extobj->x509_extension = X509_EXTENSION_dup(ext); - - return (PyObject*)extobj; -} - -/* - * ADD_METHOD(name) expands to a correct PyMethodDef declaration - * { 'name', (PyCFunction)crypto_X509_name, METH_VARARGS } - * for convenience - */ -#define ADD_METHOD(name) \ - { #name, (PyCFunction)crypto_X509_##name, METH_VARARGS, crypto_X509_##name##_doc } -static PyMethodDef crypto_X509_methods[] = -{ - ADD_METHOD(get_version), - ADD_METHOD(set_version), - ADD_METHOD(get_serial_number), - ADD_METHOD(set_serial_number), - ADD_METHOD(get_issuer), - ADD_METHOD(set_issuer), - ADD_METHOD(get_subject), - ADD_METHOD(set_subject), - ADD_METHOD(get_pubkey), - ADD_METHOD(set_pubkey), - ADD_METHOD(get_notBefore), - ADD_METHOD(set_notBefore), - ADD_METHOD(get_notAfter), - ADD_METHOD(set_notAfter), - ADD_METHOD(gmtime_adj_notBefore), - ADD_METHOD(gmtime_adj_notAfter), - ADD_METHOD(get_signature_algorithm), - ADD_METHOD(sign), - ADD_METHOD(has_expired), - ADD_METHOD(subject_name_hash), - ADD_METHOD(digest), - ADD_METHOD(add_extensions), - ADD_METHOD(get_extension), - ADD_METHOD(get_extension_count), - { NULL, NULL } -}; -#undef ADD_METHOD - - -/* - * Constructor for X509 objects, never called by Python code directly - * - * Arguments: cert - A "real" X509 certificate object - * dealloc - Boolean value to specify whether the destructor should - * free the "real" X509 object - * Returns: The newly created X509 object - */ -crypto_X509Obj * -crypto_X509_New(X509 *cert, int dealloc) -{ - crypto_X509Obj *self; - - self = PyObject_New(crypto_X509Obj, &crypto_X509_Type); - - if (self == NULL) - return NULL; - - self->x509 = cert; - self->dealloc = dealloc; - - return self; -} - - -static char crypto_X509_doc[] = "\n\ -X509() -> X509 instance\n\ -\n\ -Create a new X509 object.\n\ -\n\ -@returns: The X509 object\n\ -"; - -static PyObject * -crypto_X509_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) -{ - if (!PyArg_ParseTuple(args, ":X509")) { - return NULL; - } - - return (PyObject *)crypto_X509_New(X509_new(), 1); -} - - -/* - * Deallocate the memory used by the X509 object - * - * Arguments: self - The X509 object - * Returns: None - */ -static void -crypto_X509_dealloc(crypto_X509Obj *self) -{ - /* Sometimes we don't have to dealloc the "real" X509 pointer ourselves */ - if (self->dealloc) - X509_free(self->x509); - - PyObject_Del(self); -} - -PyTypeObject crypto_X509_Type = { - PyOpenSSL_HEAD_INIT(&PyType_Type, 0) - "X509", - sizeof(crypto_X509Obj), - 0, - (destructor)crypto_X509_dealloc, - NULL, /* print */ - NULL, /* getattr */ - NULL, /* setattr */ - NULL, /* compare */ - NULL, /* repr */ - NULL, /* as_number */ - NULL, /* as_sequence */ - NULL, /* as_mapping */ - NULL, /* hash */ - NULL, /* call */ - NULL, /* str */ - NULL, /* getattro */ - NULL, /* setattro */ - NULL, /* as_buffer */ - Py_TPFLAGS_DEFAULT, - crypto_X509_doc, /* doc */ - NULL, /* traverse */ - NULL, /* clear */ - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - crypto_X509_methods, /* tp_methods */ - NULL, /* tp_members */ - NULL, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - NULL, /* tp_init */ - NULL, /* tp_alloc */ - crypto_X509_new, /* tp_new */ -}; - -/* - * Initialize the X509 part of the crypto sub module - * - * Arguments: module - The crypto module - * Returns: None - */ -int -init_crypto_x509(PyObject *module) -{ - if (PyType_Ready(&crypto_X509_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_X509_Type); - if (PyModule_AddObject(module, "X509", (PyObject *)&crypto_X509_Type) != 0) { - return 0; - } - - Py_INCREF((PyObject *)&crypto_X509_Type); - if (PyModule_AddObject(module, "X509Type", (PyObject *)&crypto_X509_Type) != 0) { - return 0; - } - - return 1; -} - diff --git a/OpenSSL/crypto/x509.h b/OpenSSL/crypto/x509.h deleted file mode 100644 index f6cd190..0000000 --- a/OpenSSL/crypto/x509.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * x509.h - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * Export x509 functions and data structure. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - * - */ -#ifndef PyOpenSSL_crypto_X509_H_ -#define PyOpenSSL_crypto_X509_H_ - -#include -#include - -extern PyTypeObject crypto_X509_Type; - -#define crypto_X509_Check(v) ((v)->ob_type == &crypto_X509_Type) - -typedef struct { - PyObject_HEAD - X509 *x509; - int dealloc; -} crypto_X509Obj; - -PyObject* _set_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args); -PyObject* _get_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args); -extern int init_crypto_x509 (PyObject *); - - -#endif diff --git a/OpenSSL/crypto/x509ext.c b/OpenSSL/crypto/x509ext.c deleted file mode 100644 index adbe084..0000000 --- a/OpenSSL/crypto/x509ext.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * x509ext.c - * - * Copyright (C) Jean-Paul Calderone - * See LICENSE for details. - * - * Export X.509 extension functions and data structures. - * See the file RATIONALE for a short explanation of why this module was written. - * - */ - -#include -#define crypto_MODULE -#include "crypto.h" - -static char crypto_X509Extension_get_critical_doc[] = "\n\ -Returns the critical field of the X509Extension\n\ -\n\ -@return: The critical field.\n\ -"; - -static PyObject * -crypto_X509Extension_get_critical(crypto_X509ExtensionObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":get_critical")) - return NULL; - - return PyLong_FromLong(X509_EXTENSION_get_critical(self->x509_extension)); -} - -static char crypto_X509Extension_get_short_name_doc[] = "\n\ -Returns the short version of the type name of the X509Extension\n\ -\n\ -@return: The short type name.\n\ -"; - -static PyObject * -crypto_X509Extension_get_short_name(crypto_X509ExtensionObj *self, PyObject *args) { - ASN1_OBJECT *obj; - const char *extname; - - if (!PyArg_ParseTuple(args, ":get_short_name")) { - return NULL; - } - - /* Returns an internal pointer to x509_extension, not a copy */ - obj = X509_EXTENSION_get_object(self->x509_extension); - - extname = OBJ_nid2sn(OBJ_obj2nid(obj)); - return PyBytes_FromString(extname); -} - - -static char crypto_X509Extension_get_data_doc[] = "\n\ -Returns the data of the X509Extension\n\ -\n\ -@return: A C{str} giving the X509Extension's ASN.1 encoded data.\n\ -"; - -static PyObject * -crypto_X509Extension_get_data(crypto_X509ExtensionObj *self, PyObject *args) { - ASN1_OCTET_STRING *data; - PyObject *result; - - if (!PyArg_ParseTuple(args, ":get_data")) { - return NULL; - } - - data = X509_EXTENSION_get_data(self->x509_extension); - result = PyBytes_FromStringAndSize((const char*)data->data, data->length); - return result; -} - -/* - * ADD_METHOD(name) expands to a correct PyMethodDef declaration - * { 'name', (PyCFunction)crypto_X509Extension_name, METH_VARARGS } - * for convenience - */ -#define ADD_METHOD(name) \ -{ #name, (PyCFunction)crypto_X509Extension_##name, METH_VARARGS, crypto_X509Extension_##name##_doc } -static PyMethodDef crypto_X509Extension_methods[] = -{ - ADD_METHOD(get_critical), - ADD_METHOD(get_short_name), - ADD_METHOD(get_data), - { NULL, NULL } -}; -#undef ADD_METHOD - -/* - * Constructor for X509Extension, never called by Python code directly - * - * Arguments: type_name - ??? - * critical - ??? - * value - ??? - * subject - An x509v3 certificate which is the subject for this extension. - * issuer - An x509v3 certificate which is the issuer for this extension. - * Returns: The newly created X509Extension object - */ -crypto_X509ExtensionObj * -crypto_X509Extension_New(char *type_name, int critical, char *value, - crypto_X509Obj *subject, crypto_X509Obj *issuer) { - X509V3_CTX ctx; - crypto_X509ExtensionObj *self; - char* value_with_critical = NULL; - - - /* - * A context is necessary for any extension which uses the r2i conversion - * method. That is, X509V3_EXT_nconf may segfault if passed a NULL ctx. - * Start off by initializing most of the fields to NULL. - */ - X509V3_set_ctx(&ctx, NULL, NULL, NULL, NULL, 0); - - /* - * We have no configuration database - but perhaps we should (some - * extensions may require it). - */ - X509V3_set_ctx_nodb(&ctx); - - /* - * Initialize the subject and issuer, if appropriate. ctx is a local, and - * as far as I can tell none of the X509V3_* APIs invoked here steal any - * references, so no need to incref subject or issuer. - */ - if (subject) { - ctx.subject_cert = subject->x509; - } - - if (issuer) { - ctx.issuer_cert = issuer->x509; - } - - self = PyObject_New(crypto_X509ExtensionObj, &crypto_X509Extension_Type); - - if (self == NULL) { - goto error; - } - - self->dealloc = 0; - - /* There are other OpenSSL APIs which would let us pass in critical - * separately, but they're harder to use, and since value is already a pile - * of crappy junk smuggling a ton of utterly important structured data, - * what's the point of trying to avoid nasty stuff with strings? (However, - * X509V3_EXT_i2d in particular seems like it would be a better API to - * invoke. I do not know where to get the ext_struc it desires for its - * last parameter, though.) */ - value_with_critical = malloc(strlen("critical,") + strlen(value) + 1); - if (!value_with_critical) { - goto critical_malloc_error; - } - - if (critical) { - strcpy(value_with_critical, "critical,"); - strcpy(value_with_critical + strlen("critical,"), value); - } else { - strcpy(value_with_critical, value); - } - - self->x509_extension = X509V3_EXT_nconf( - NULL, &ctx, type_name, value_with_critical); - - free(value_with_critical); - - if (!self->x509_extension) { - goto nconf_error; - } - - self->dealloc = 1; - return self; - - nconf_error: - exception_from_error_queue(crypto_Error); - - critical_malloc_error: - Py_XDECREF(self); - - error: - return NULL; - -} - -static char crypto_X509Extension_doc[] = "\n\ -X509Extension(typename, critical, value[, subject][, issuer]) -> \n\ - X509Extension instance\n\ -\n\ -@param typename: The name of the extension to create.\n\ -@type typename: C{str}\n\ -@param critical: A flag indicating whether this is a critical extension.\n\ -@param value: The value of the extension.\n\ -@type value: C{str}\n\ -@param subject: Optional X509 cert to use as subject.\n\ -@type subject: C{X509}\n\ -@param issuer: Optional X509 cert to use as issuer.\n\ -@type issuer: C{X509}\n\ -@return: The X509Extension object\n\ -"; - -static PyObject * -crypto_X509Extension_new(PyTypeObject *subtype, PyObject *args, - PyObject *kwargs) { - char *type_name, *value; - int critical = 0; - crypto_X509Obj * subject = NULL; - crypto_X509Obj * issuer = NULL; - static char *kwlist[] = {"type_name", "critical", "value", "subject", - "issuer", NULL}; - - if (!PyArg_ParseTupleAndKeywords( - args, kwargs, - BYTESTRING_FMT "i" BYTESTRING_FMT "|O!O!:X509Extension", - kwlist, &type_name, &critical, &value, - &crypto_X509_Type, &subject, - &crypto_X509_Type, &issuer )) { - return NULL; - } - - return (PyObject *)crypto_X509Extension_New(type_name, critical, value, - subject, issuer); -} - -/* - * Deallocate the memory used by the X509Extension object - * - * Arguments: self - The X509Extension object - * Returns: None - */ -static void -crypto_X509Extension_dealloc(crypto_X509ExtensionObj *self) -{ - /* Sometimes we don't have to dealloc this */ - if (self->dealloc) - X509_EXTENSION_free(self->x509_extension); - - PyObject_Del(self); -} - -/* - * Print a nice text representation of the certificate request. - */ -static PyObject * -crypto_X509Extension_str(crypto_X509ExtensionObj *self) -{ - int str_len; - char *tmp_str; - PyObject *str; - BIO *bio = BIO_new(BIO_s_mem()); - - if (!X509V3_EXT_print(bio, self->x509_extension, 0, 0)) - { - BIO_free(bio); - exception_from_error_queue(crypto_Error); - return NULL; - } - - str_len = BIO_get_mem_data(bio, &tmp_str); - str = PyText_FromStringAndSize(tmp_str, str_len); - - BIO_free(bio); - - return str; -} - -PyTypeObject crypto_X509Extension_Type = { - PyOpenSSL_HEAD_INIT(&PyType_Type, 0) - "X509Extension", - sizeof(crypto_X509ExtensionObj), - 0, - (destructor)crypto_X509Extension_dealloc, - NULL, /* print */ - NULL, /* getattr */ - NULL, /* setattr (setattrfunc)crypto_X509Name_setattr, */ - NULL, /* compare */ - NULL, /* repr */ - NULL, /* as_number */ - NULL, /* as_sequence */ - NULL, /* as_mapping */ - NULL, /* hash */ - NULL, /* call */ - (reprfunc)crypto_X509Extension_str, /* str */ - NULL, /* getattro */ - NULL, /* setattro */ - NULL, /* as_buffer */ - Py_TPFLAGS_DEFAULT, - crypto_X509Extension_doc, /* doc */ - NULL, /* traverse */ - NULL, /* clear */ - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - crypto_X509Extension_methods, /* tp_methods */ - NULL, /* tp_members */ - NULL, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - NULL, /* tp_init */ - NULL, /* tp_alloc */ - crypto_X509Extension_new, /* tp_new */ -}; - -/* - * Initialize the X509Extension part of the crypto module - * - * Arguments: dict - The crypto module - * Returns: None - */ -int -init_crypto_x509extension(PyObject *module) -{ - if (PyType_Ready(&crypto_X509Extension_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_X509Extension_Type); - if (PyModule_AddObject(module, "X509Extension", - (PyObject *)&crypto_X509Extension_Type) != 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_X509Extension_Type); - if (PyModule_AddObject(module, "X509ExtensionType", - (PyObject *)&crypto_X509Extension_Type) != 0) { - return 0; - } - - return 1; -} diff --git a/OpenSSL/crypto/x509ext.h b/OpenSSL/crypto/x509ext.h deleted file mode 100644 index 3ddc716..0000000 --- a/OpenSSL/crypto/x509ext.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * x509ext.h - * - * Copyright (C) Awanim - * See LICENSE for details. - * - * Export X.509 extension functions and data structures. - * See the file RATIONALE for a short explanation of why this module was written. - * - */ -#ifndef PyOpenSSL_crypto_X509EXTENSION_H_ -#define PyOpenSSL_crypto_X509EXTENSION_H_ - -#include -#include -#include - -extern int init_crypto_x509extension (PyObject *); - -extern PyTypeObject crypto_X509Extension_Type; - -#define crypto_X509Extension_Check(v) ( \ - PyObject_TypeCheck((v), \ - &crypto_X509Extension_Type)) - -typedef struct { - PyObject_HEAD - X509_EXTENSION *x509_extension; - int dealloc; -} crypto_X509ExtensionObj; - -#endif - diff --git a/OpenSSL/crypto/x509name.c b/OpenSSL/crypto/x509name.c deleted file mode 100644 index a62c957..0000000 --- a/OpenSSL/crypto/x509name.c +++ /dev/null @@ -1,555 +0,0 @@ -/* - * x509name.c - * - * Copyright (C) AB Strakt - * Copyright (C) Jean-Paul Calderone - * See LICENSE for details. - * - * X.509 Name handling, mostly thin wrapping. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - */ -#include -#define crypto_MODULE -#include "crypto.h" - -static PyMethodDef crypto_X509Name_methods[4]; - -/* - * Constructor for X509Name, never called by Python code directly - * - * Arguments: name - A "real" X509_NAME object - * dealloc - Boolean value to specify whether the destructor should - * free the "real" X509_NAME object - * Returns: The newly created X509Name object - */ -crypto_X509NameObj * -crypto_X509Name_New(X509_NAME *name, int dealloc) -{ - crypto_X509NameObj *self; - - self = PyObject_GC_New(crypto_X509NameObj, &crypto_X509Name_Type); - - if (self == NULL) - return NULL; - - self->x509_name = name; - self->dealloc = dealloc; - self->parent_cert = NULL; - - PyObject_GC_Track(self); - return self; -} - - -static char crypto_X509Name_doc[] = "\n\ -X509Name(name) -> New X509Name object\n\ -\n\ -Create a new X509Name, copying the given X509Name instance.\n\ -\n\ -@param name: An X509Name object to copy\n\ -@return: The X509Name object\n\ -"; - -static PyObject * -crypto_X509Name_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) -{ - crypto_X509NameObj *name; - - if (!PyArg_ParseTuple(args, "O!:X509Name", &crypto_X509Name_Type, &name)) { - return NULL; - } - - return (PyObject *)crypto_X509Name_New(X509_NAME_dup(name->x509_name), 1); -} - - -/* - * Return a name string given a X509_NAME object and a name identifier. Used - * by the getattr function. - * - * Arguments: name - The X509_NAME object - * nid - The name identifier - * Returns: The name as a Python string object - */ -static int -get_name_by_nid(X509_NAME *name, int nid, char **utf8string) -{ - int entry_idx; - X509_NAME_ENTRY *entry; - ASN1_STRING *data; - int len; - - if ((entry_idx = X509_NAME_get_index_by_NID(name, nid, -1)) == -1) - { - return 0; - } - entry = X509_NAME_get_entry(name, entry_idx); - data = X509_NAME_ENTRY_get_data(entry); - if ((len = ASN1_STRING_to_UTF8((unsigned char **)utf8string, data)) < 0) - { - exception_from_error_queue(crypto_Error); - return -1; - } - - return len; -} - -/* - * Given a X509_NAME object and a name identifier, set the corresponding - * attribute to the given string. Used by the setattr function. - * - * Arguments: name - The X509_NAME object - * nid - The name identifier - * value - The string to set - * Returns: 0 for success, -1 on failure - */ -static int -set_name_by_nid(X509_NAME *name, int nid, char *utf8string) -{ - X509_NAME_ENTRY *ne; - int i, entry_count, temp_nid; - - /* If there's an old entry for this NID, remove it */ - entry_count = X509_NAME_entry_count(name); - for (i = 0; i < entry_count; i++) - { - ne = X509_NAME_get_entry(name, i); - temp_nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(ne)); - if (temp_nid == nid) - { - ne = X509_NAME_delete_entry(name, i); - X509_NAME_ENTRY_free(ne); - break; - } - } - - /* Add the new entry */ - if (!X509_NAME_add_entry_by_NID(name, nid, MBSTRING_UTF8, - (unsigned char *)utf8string, - -1, -1, 0)) - { - exception_from_error_queue(crypto_Error); - return -1; - } - return 0; -} - - -/* - * Find attribute. An X509Name object has the following attributes: - * countryName (alias C), stateOrProvince (alias ST), locality (alias L), - * organization (alias O), organizationalUnit (alias OU), commonName (alias - * CN) and more... - * - * Arguments: self - The X509Name object - * name - The attribute name - * Returns: A Python object for the attribute, or NULL if something went - * wrong - */ -static PyObject * -crypto_X509Name_getattro(crypto_X509NameObj *self, PyObject *nameobj) -{ - int nid, len; - char *utf8string; - char *name; -#ifdef PY3 - name = PyBytes_AsString(PyUnicode_AsASCIIString(nameobj)); -#else - name = PyBytes_AsString(nameobj); -#endif - - if ((nid = OBJ_txt2nid(name)) == NID_undef) { - /* - * This is a bit weird. OBJ_txt2nid indicated failure, but it seems - * a lower level function, a2d_ASN1_OBJECT, also feels the need to - * push something onto the error queue. If we don't clean that up - * now, someone else will bump into it later and be quite confused. - * See lp#314814. - */ - flush_error_queue(); - return PyObject_GenericGetAttr((PyObject*)self, nameobj); - } - - len = get_name_by_nid(self->x509_name, nid, &utf8string); - if (len < 0) - return NULL; - else if (len == 0) - { - Py_INCREF(Py_None); - return Py_None; - } - else { - PyObject* result = PyUnicode_Decode(utf8string, len, "utf-8", NULL); - OPENSSL_free(utf8string); - return result; - } -} - -/* - * Set attribute - * - * Arguments: self - The X509Name object - * name - The attribute name - * value - The value to set - */ -static int -crypto_X509Name_setattro(crypto_X509NameObj *self, PyObject *nameobj, PyObject *value) -{ - int nid; - int result; - char *buffer; - char *name; - - if (!PyBytes_CheckExact(nameobj) && !PyUnicode_CheckExact(nameobj)) { - PyErr_Format(PyExc_TypeError, - "attribute name must be string, not '%.200s'", - Py_TYPE(nameobj)->tp_name); - return -1; - } - -#ifdef PY3 - name = PyBytes_AsString(PyUnicode_AsASCIIString(nameobj)); -#else - name = PyBytes_AsString(nameobj); -#endif - - if ((nid = OBJ_txt2nid(name)) == NID_undef) - { - /* Just like the case in the getattr function */ - flush_error_queue(); - PyErr_SetString(PyExc_AttributeError, "No such attribute"); - return -1; - } - - /* Something of a hack to get nice unicode behaviour */ - if (!PyArg_Parse(value, "es:setattr", "utf-8", &buffer)) - return -1; - - result = set_name_by_nid(self->x509_name, nid, buffer); - PyMem_Free(buffer); - return result; -} - -/* - * Compare two X509Name structures. - * - * Arguments: n - The first X509Name - * m - The second X509Name - * Returns: <0 if n < m, 0 if n == m and >0 if n > m - */ -static PyObject * -crypto_X509Name_richcompare(PyObject *n, PyObject *m, int op) { - int result; - - if (!crypto_X509Name_Check(n) || !crypto_X509Name_Check(m)) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - - result = X509_NAME_cmp( - ((crypto_X509NameObj*)n)->x509_name, - ((crypto_X509NameObj*)m)->x509_name); - - switch (op) { - case Py_EQ: - result = (result == 0); - break; - - case Py_NE: - result = (result != 0); - break; - - case Py_LT: - result = (result < 0); - break; - - case Py_LE: - result = (result <= 0); - break; - - case Py_GT: - result = (result > 0); - break; - - case Py_GE: - result = (result >= 0); - break; - - default: - /* Should be impossible */ - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - - if (result) { - Py_INCREF(Py_True); - return Py_True; - } else { - Py_INCREF(Py_False); - return Py_False; - } -} - -/* - * String representation of an X509Name - * - * Arguments: self - The X509Name object - * Returns: A string representation of the object - */ -static PyObject * -crypto_X509Name_repr(crypto_X509NameObj *self) -{ - char tmpbuf[512] = ""; - char realbuf[512+64]; - - if (X509_NAME_oneline(self->x509_name, tmpbuf, 512) == NULL) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - else - { - /* This is safe because tmpbuf is max 512 characters */ - sprintf(realbuf, "", tmpbuf); - return PyText_FromString(realbuf); - } -} - -static char crypto_X509Name_hash_doc[] = "\n\ -Return the hash value of this name\n\ -\n\ -@return: None\n\ -"; - -/* - * First four bytes of the MD5 digest of the DER form of an X509Name. - * - * Arguments: self - The X509Name object - * Returns: An integer giving the hash. - */ -static PyObject * -crypto_X509Name_hash(crypto_X509NameObj *self, PyObject* args) -{ - unsigned long hash; - - if (!PyArg_ParseTuple(args, ":hash")) { - return NULL; - } - hash = X509_NAME_hash(self->x509_name); - return PyLong_FromLong(hash); -} - -static char crypto_X509Name_der_doc[] = "\n\ -Return the DER encoding of this name\n\ -\n\ -@return: None\n\ -"; - -/* - * Arguments: self - The X509Name object - * Returns: The DER form of an X509Name. - */ -static PyObject * -crypto_X509Name_der(crypto_X509NameObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":der")) { - return NULL; - } - - i2d_X509_NAME(self->x509_name, 0); - return PyBytes_FromStringAndSize(self->x509_name->bytes->data, - self->x509_name->bytes->length); -} - - -static char crypto_X509Name_get_components_doc[] = "\n\ -Returns the split-up components of this name.\n\ -\n\ -@return: List of tuples (name, value).\n\ -"; - -static PyObject * -crypto_X509Name_get_components(crypto_X509NameObj *self, PyObject *args) -{ - int n, i; - X509_NAME *name = self->x509_name; - PyObject *list; - - if (!PyArg_ParseTuple(args, ":get_components")) - return NULL; - - n = X509_NAME_entry_count(name); - list = PyList_New(n); - for (i = 0; i < n; i++) - { - X509_NAME_ENTRY *ent; - ASN1_OBJECT *fname; - ASN1_STRING *fval; - int nid; - int l; - unsigned char *str; - PyObject *tuple; - - ent = X509_NAME_get_entry(name, i); - - fname = X509_NAME_ENTRY_get_object(ent); - fval = X509_NAME_ENTRY_get_data(ent); - - l = ASN1_STRING_length(fval); - str = ASN1_STRING_data(fval); - - nid = OBJ_obj2nid(fname); - - /* printf("fname is %s len=%d str=%s\n", OBJ_nid2sn(nid), l, str); */ - - tuple = PyTuple_New(2); - PyTuple_SetItem(tuple, 0, PyBytes_FromString(OBJ_nid2sn(nid))); - PyTuple_SetItem(tuple, 1, PyBytes_FromStringAndSize((char *)str, l)); - - PyList_SetItem(list, i, tuple); - } - - return list; -} - - -/* - * Call the visitproc on all contained objects. - * - * Arguments: self - The Connection object - * visit - Function to call - * arg - Extra argument to visit - * Returns: 0 if all goes well, otherwise the return code from the first - * call that gave non-zero result. - */ -static int -crypto_X509Name_traverse(crypto_X509NameObj *self, visitproc visit, void *arg) -{ - int ret = 0; - - if (ret == 0 && self->parent_cert != NULL) - ret = visit(self->parent_cert, arg); - return ret; -} - -/* - * Decref all contained objects and zero the pointers. - * - * Arguments: self - The Connection object - * Returns: Always 0. - */ -static int -crypto_X509Name_clear(crypto_X509NameObj *self) -{ - Py_XDECREF(self->parent_cert); - self->parent_cert = NULL; - return 0; -} - -/* - * Deallocate the memory used by the X509Name object - * - * Arguments: self - The X509Name object - * Returns: None - */ -static void -crypto_X509Name_dealloc(crypto_X509NameObj *self) -{ - PyObject_GC_UnTrack(self); - /* Sometimes we don't have to dealloc this */ - if (self->dealloc) - X509_NAME_free(self->x509_name); - - crypto_X509Name_clear(self); - - PyObject_GC_Del(self); -} - -/* - * ADD_METHOD(name) expands to a correct PyMethodDef declaration - * { 'name', (PyCFunction)crypto_X509_name, METH_VARARGS } - * for convenience - */ -#define ADD_METHOD(name) \ - { #name, (PyCFunction)crypto_X509Name_##name, METH_VARARGS, crypto_X509Name_##name##_doc } -static PyMethodDef crypto_X509Name_methods[] = -{ - ADD_METHOD(hash), - ADD_METHOD(der), - ADD_METHOD(get_components), - { NULL, NULL } -}; -#undef ADD_METHOD - -PyTypeObject crypto_X509Name_Type = { - PyOpenSSL_HEAD_INIT(&PyType_Type, 0) - "X509Name", - sizeof(crypto_X509NameObj), - 0, - (destructor)crypto_X509Name_dealloc, - NULL, /* print */ - NULL, /* getattr */ - NULL, /* setattr */ - NULL, /* reserved */ - (reprfunc)crypto_X509Name_repr, - NULL, /* as_number */ - NULL, /* as_sequence */ - NULL, /* as_mapping */ - NULL, /* hash */ - NULL, /* call */ - NULL, /* str */ - (getattrofunc)crypto_X509Name_getattro, /* getattro */ - (setattrofunc)crypto_X509Name_setattro, /* setattro */ - NULL, /* as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - crypto_X509Name_doc, /* tp_doc */ - (traverseproc)crypto_X509Name_traverse, /* tp_traverse */ - (inquiry)crypto_X509Name_clear, /* tp_clear */ - crypto_X509Name_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - crypto_X509Name_methods, /* tp_methods */ - NULL, /* tp_members */ - NULL, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - NULL, /* tp_init */ - NULL, /* tp_alloc */ - crypto_X509Name_new, /* tp_new */ -}; - -/* - * Initialize the X509Name part of the crypto module - * - * Arguments: module - The crypto module - * Returns: None - */ -int -init_crypto_x509name(PyObject *module) -{ - if (PyType_Ready(&crypto_X509Name_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_X509Name_Type); - if (PyModule_AddObject(module, "X509Name", (PyObject *)&crypto_X509Name_Type) != 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_X509Name_Type); - if (PyModule_AddObject(module, "X509NameType", (PyObject *)&crypto_X509Name_Type) != 0) { - return 0; - } - - return 1; -} diff --git a/OpenSSL/crypto/x509name.h b/OpenSSL/crypto/x509name.h deleted file mode 100644 index bfc7628..0000000 --- a/OpenSSL/crypto/x509name.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * x509name.h - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * Export X.509 name functions and data structures. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - * - */ -#ifndef PyOpenSSL_crypto_X509NAME_H_ -#define PyOpenSSL_crypto_X509NAME_H_ - -#include -#include - -extern int init_crypto_x509name (PyObject *); - -extern PyTypeObject crypto_X509Name_Type; - -#define crypto_X509Name_Check(v) ((v)->ob_type == &crypto_X509Name_Type) - -typedef struct { - PyObject_HEAD - X509_NAME *x509_name; - int dealloc; - PyObject *parent_cert; -} crypto_X509NameObj; - - -#endif diff --git a/OpenSSL/crypto/x509req.c b/OpenSSL/crypto/x509req.c deleted file mode 100644 index a2d1f11..0000000 --- a/OpenSSL/crypto/x509req.c +++ /dev/null @@ -1,431 +0,0 @@ -/* - * x509req.c - * - * Copyright (C) AB Strakt - * Copyright (C) Jean-Paul Calderone - * See LICENSE for details. - * - * X.509 Request handling, mostly thin wrapping. - * See the file RATIONALE for a short explanation of why this module was written. - */ -#include -#define crypto_MODULE -#include "crypto.h" - - -static char crypto_X509Req_get_subject_doc[] = "\n\ -Create an X509Name object for the subject of the certificate request\n\ -\n\ -@return: An X509Name object\n\ -"; - -static PyObject * -crypto_X509Req_get_subject(crypto_X509ReqObj *self, PyObject *args) -{ - crypto_X509NameObj *crypto_X509Name_New(X509_NAME *, int); - X509_NAME *name; - crypto_X509NameObj* pyname; - - if (!PyArg_ParseTuple(args, ":get_subject")) - return NULL; - - if ((name = X509_REQ_get_subject_name(self->x509_req)) == NULL) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - if ((pyname = crypto_X509Name_New(name, 0)) != NULL) { - pyname->parent_cert = (PyObject *)self; - Py_INCREF(self); - } - return (PyObject *)pyname; -} - -static char crypto_X509Req_get_pubkey_doc[] = "\n\ -Get the public key from the certificate request\n\ -\n\ -@return: The public key\n\ -"; - -static PyObject * -crypto_X509Req_get_pubkey(crypto_X509ReqObj *self, PyObject *args) -{ - crypto_PKeyObj *crypto_PKey_New(EVP_PKEY *, int); - EVP_PKEY *pkey; - crypto_PKeyObj *py_pkey; - - if (!PyArg_ParseTuple(args, ":get_pubkey")) - return NULL; - - if ((pkey = X509_REQ_get_pubkey(self->x509_req)) == NULL) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - py_pkey = crypto_PKey_New(pkey, 1); - if (py_pkey != NULL) { - py_pkey->only_public = 1; - } - return (PyObject *)py_pkey; -} - -static char crypto_X509Req_set_pubkey_doc[] = "\n\ -Set the public key of the certificate request\n\ -\n\ -@param pkey: The public key to use\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509Req_set_pubkey(crypto_X509ReqObj *self, PyObject *args) -{ - crypto_PKeyObj *pkey; - - if (!PyArg_ParseTuple(args, "O!:set_pubkey", &crypto_PKey_Type, &pkey)) - return NULL; - - if (!X509_REQ_set_pubkey(self->x509_req, pkey->pkey)) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_X509Req_sign_doc[] = "\n\ -Sign the certificate request using the supplied key and digest\n\ -\n\ -@param pkey: The key to sign with\n\ -@param digest: The message digest to use\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509Req_sign(crypto_X509ReqObj *self, PyObject *args) -{ - crypto_PKeyObj *pkey; - char *digest_name; - const EVP_MD *digest; - - if (!PyArg_ParseTuple(args, "O!s:sign", &crypto_PKey_Type, &pkey, - &digest_name)) - return NULL; - - if (pkey->only_public) { - PyErr_SetString(PyExc_ValueError, "Key has only public part"); - return NULL; - } - - if (!pkey->initialized) { - PyErr_SetString(PyExc_ValueError, "Key is uninitialized"); - return NULL; - } - - if ((digest = EVP_get_digestbyname(digest_name)) == NULL) - { - PyErr_SetString(PyExc_ValueError, "No such digest method"); - return NULL; - } - - if (!X509_REQ_sign(self->x509_req, pkey->pkey, digest)) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_X509Req_verify_doc[] = "\n\ -Verifies a certificate request using the supplied public key\n\ -\n\ -@param key: a public key\n\ -@return: True if the signature is correct.\n\ -@raise OpenSSL.crypto.Error: If the signature is invalid or there is a\n\ - problem verifying the signature.\n\ -"; - -PyObject * -crypto_X509Req_verify(crypto_X509ReqObj *self, PyObject *args) -{ - PyObject *obj; - crypto_PKeyObj *key; - int answer; - - if (!PyArg_ParseTuple(args, "O!:verify", &crypto_PKey_Type, &obj)) { - return NULL; - } - - key = (crypto_PKeyObj *)obj; - - if ((answer = X509_REQ_verify(self->x509_req, key->pkey)) <= 0) { - exception_from_error_queue(crypto_Error); - return NULL; - } - - return PyLong_FromLong(answer); -} - -static char crypto_X509Req_add_extensions_doc[] = "\n\ -Add extensions to the request.\n\ -\n\ -@param extensions: a sequence of X509Extension objects\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509Req_add_extensions(crypto_X509ReqObj *self, PyObject *args) -{ - PyObject *extensions; - crypto_X509ExtensionObj *ext; - STACK_OF(X509_EXTENSION) *exts; - int nr_of_extensions, i; - - if (!PyArg_ParseTuple(args, "O:add_extensions", &extensions)) - return NULL; - - if (!PySequence_Check(extensions)) - { - PyErr_SetString(PyExc_TypeError, "Expected a sequence"); - return NULL; - } - - /* Make a STACK_OF(X509_EXTENSION) from sequence */ - if ((exts = sk_X509_EXTENSION_new_null()) == NULL) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - /* Put the extensions in a stack */ - nr_of_extensions = PySequence_Length(extensions); - - for (i = 0; i < nr_of_extensions; i++) - { - ext = (crypto_X509ExtensionObj *)PySequence_GetItem(extensions, i); - if (!(crypto_X509Extension_Check(ext))) - { - PyErr_SetString(PyExc_ValueError, - "One of the elements is not an X509Extension"); - sk_X509_EXTENSION_free(exts); - return NULL; - } - sk_X509_EXTENSION_push(exts, ext->x509_extension); - } - - if (!X509_REQ_add_extensions(self->x509_req, exts)) - { - sk_X509_EXTENSION_free(exts); - exception_from_error_queue(crypto_Error); - return NULL; - } - - sk_X509_EXTENSION_free(exts); - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_X509Req_set_version_doc[] = "\n\ -Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate\n\ -request.\n\ -\n\ -@param version: The version number\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509Req_set_version(crypto_X509ReqObj *self, PyObject *args) -{ - long version; - - if (!PyArg_ParseTuple(args, "l:set_version", &version)) { - return NULL; - } - - if (!X509_REQ_set_version(self->x509_req, version)) { - return NULL; - } - - Py_INCREF(Py_None); - return Py_None; -} - -static char crypto_X509Req_get_version_doc[] = "\n\ -Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate\n\ -request.\n\ -\n\ -@return: an integer giving the value of the version subfield\n\ -"; - -static PyObject * -crypto_X509Req_get_version(crypto_X509ReqObj *self, PyObject *args) -{ - long version; - - if (!PyArg_ParseTuple(args, ":get_version")) { - return NULL; - } - - version = X509_REQ_get_version(self->x509_req); - - return PyLong_FromLong(version); -} - -/* - * ADD_METHOD(name) expands to a correct PyMethodDef declaration - * { 'name', (PyCFunction)crypto_X509Req_name, METH_VARARGS } - * for convenience - */ -#define ADD_METHOD(name) \ - { #name, (PyCFunction)crypto_X509Req_##name, METH_VARARGS, crypto_X509Req_##name##_doc } -static PyMethodDef crypto_X509Req_methods[] = -{ - ADD_METHOD(get_subject), - ADD_METHOD(get_pubkey), - ADD_METHOD(set_pubkey), - ADD_METHOD(sign), - ADD_METHOD(verify), - ADD_METHOD(add_extensions), - ADD_METHOD(set_version), - ADD_METHOD(get_version), - { NULL, NULL } -}; -#undef ADD_METHOD - - -/* - * Constructor for X509Req, never called by Python code directly - * - * Arguments: name - A "real" X509_REQ object - * dealloc - Boolean value to specify whether the destructor should - * free the "real" X509_REQ object - * Returns: The newly created X509Req object - */ -crypto_X509ReqObj * -crypto_X509Req_New(X509_REQ *req, int dealloc) -{ - crypto_X509ReqObj *self; - - self = PyObject_New(crypto_X509ReqObj, &crypto_X509Req_Type); - - if (self == NULL) - return NULL; - - self->x509_req = req; - self->dealloc = dealloc; - - return self; -} - - -static char crypto_X509Req_doc[] = "\n\ -X509Req() -> X509Req instance\n\ -\n\ -Create a new X509Req object.\n\ -\n\ -@return: The X509Req object\n\ -"; - -static PyObject * -crypto_X509Req_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { - if (!PyArg_ParseTuple(args, ":X509Req")) { - return NULL; - } - - return (PyObject *)crypto_X509Req_New(X509_REQ_new(), 1); -} - - -/* - * Deallocate the memory used by the X509Req object - * - * Arguments: self - The X509Req object - * Returns: None - */ -static void -crypto_X509Req_dealloc(crypto_X509ReqObj *self) -{ - /* Sometimes we don't have to dealloc this */ - if (self->dealloc) - X509_REQ_free(self->x509_req); - - PyObject_Del(self); -} - - -PyTypeObject crypto_X509Req_Type = { - PyOpenSSL_HEAD_INIT(&PyType_Type, 0) - "X509Req", - sizeof(crypto_X509ReqObj), - 0, - (destructor)crypto_X509Req_dealloc, - NULL, /* print */ - NULL, /* getattr */ - NULL, /* setattr */ - NULL, /* compare */ - NULL, /* repr */ - NULL, /* as_number */ - NULL, /* as_sequence */ - NULL, /* as_mapping */ - NULL, /* hash */ - NULL, /* call */ - NULL, /* str */ - NULL, /* getattro */ - NULL, /* setattro */ - NULL, /* as_buffer */ - Py_TPFLAGS_DEFAULT, - crypto_X509Req_doc, /* doc */ - NULL, /* traverse */ - NULL, /* clear */ - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - crypto_X509Req_methods, /* tp_methods */ - NULL, /* tp_members */ - NULL, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - NULL, /* tp_init */ - NULL, /* tp_alloc */ - crypto_X509Req_new, /* tp_new */ -}; - - -/* - * Initialize the X509Req part of the crypto module - * - * Arguments: module - The crypto module - * Returns: None - */ -int -init_crypto_x509req(PyObject *module) -{ - if (PyType_Ready(&crypto_X509Req_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_X509Req_Type); - if (PyModule_AddObject(module, "X509Req", (PyObject *)&crypto_X509Req_Type) != 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_X509Req_Type); - if (PyModule_AddObject(module, "X509ReqType", (PyObject *)&crypto_X509Req_Type) != 0) { - return 0; - } - - return 1; -} diff --git a/OpenSSL/crypto/x509req.h b/OpenSSL/crypto/x509req.h deleted file mode 100644 index 5fe0524..0000000 --- a/OpenSSL/crypto/x509req.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * x509req.h - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * Export X509 request functions and data structures. - * See the file RATIONALE for a short explanation of why this module was written. - * - */ -#ifndef PyOpenSSL_SSL_X509REQ_H_ -#define PyOpenSSL_SSL_X509REQ_H_ - -#include -#include - -extern int init_crypto_x509req (PyObject *); - -extern PyTypeObject crypto_X509Req_Type; - -#define crypto_X509Req_Check(v) ((v)->ob_type == &crypto_X509Req_Type) - -typedef struct { - PyObject_HEAD - X509_REQ *x509_req; - int dealloc; -} crypto_X509ReqObj; - - -#endif diff --git a/OpenSSL/crypto/x509store.c b/OpenSSL/crypto/x509store.c deleted file mode 100644 index bf22756..0000000 --- a/OpenSSL/crypto/x509store.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * x509store.c - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * X.509 Store handling, mostly thin wrapping. - * See the file RATIONALE for a short explanation of why this module was written. - */ -#include -#define crypto_MODULE -#include "crypto.h" - -static char crypto_X509Store_add_cert_doc[] = "\n\ -Add a certificate\n\ -\n\ -@param cert: The certificate to add\n\ -@return: None\n\ -"; - -static PyObject * -crypto_X509Store_add_cert(crypto_X509StoreObj *self, PyObject *args) -{ - crypto_X509Obj *cert; - - if (!PyArg_ParseTuple(args, "O!:add_cert", &crypto_X509_Type, &cert)) - return NULL; - - if (!X509_STORE_add_cert(self->x509_store, cert->x509)) - { - exception_from_error_queue(crypto_Error); - return NULL; - } - - Py_INCREF(Py_None); - return Py_None; -} - - -/* - * ADD_METHOD(name) expands to a correct PyMethodDef declaration - * { 'name', (PyCFunction)crypto_X509Store_name, METH_VARARGS } - * for convenience - */ -#define ADD_METHOD(name) \ - { #name, (PyCFunction)crypto_X509Store_##name, METH_VARARGS, crypto_X509Store_##name##_doc } -static PyMethodDef crypto_X509Store_methods[] = -{ - ADD_METHOD(add_cert), - { NULL, NULL } -}; -#undef ADD_METHOD - - -/* - * Constructor for X509Store, never called by Python code directly - * - * Arguments: name - A "real" X509_STORE object - * dealloc - Boolean value to specify whether the destructor should - * free the "real" X509_STORE object - * Returns: The newly created X509Store object - */ -crypto_X509StoreObj * -crypto_X509Store_New(X509_STORE *store, int dealloc) -{ - crypto_X509StoreObj *self; - - self = PyObject_New(crypto_X509StoreObj, &crypto_X509Store_Type); - - if (self == NULL) - return NULL; - - self->x509_store = store; - self->dealloc = dealloc; - - return self; -} - -/* - * Deallocate the memory used by the X509Store object - * - * Arguments: self - The X509Store object - * Returns: None - */ -static void -crypto_X509Store_dealloc(crypto_X509StoreObj *self) -{ - /* Sometimes we don't have to dealloc this */ - if (self->dealloc) - X509_STORE_free(self->x509_store); - - PyObject_Del(self); -} - - -PyTypeObject crypto_X509Store_Type = { - PyOpenSSL_HEAD_INIT(&PyType_Type, 0) - "X509Store", - sizeof(crypto_X509StoreObj), - 0, - (destructor)crypto_X509Store_dealloc, - NULL, /* print */ - NULL, /* getattr */ - NULL, /* setattr */ - NULL, /* compare */ - NULL, /* repr */ - NULL, /* as_number */ - NULL, /* as_sequence */ - NULL, /* as_mapping */ - NULL, /* hash */ - NULL, /* call */ - NULL, /* str */ - NULL, /* getattro */ - NULL, /* setattro */ - NULL, /* as_buffer */ - Py_TPFLAGS_DEFAULT, - NULL, /* doc */ - NULL, /* traverse */ - NULL, /* clear */ - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - crypto_X509Store_methods, /* tp_methods */ -}; - - -/* - * Initialize the X509Store part of the crypto module - * - * Arguments: module - The crypto module - * Returns: None - */ -int -init_crypto_x509store(PyObject *module) -{ - if (PyType_Ready(&crypto_X509Store_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&crypto_X509Store_Type); - if (PyModule_AddObject(module, "X509StoreType", (PyObject *)&crypto_X509Store_Type) != 0) { - return 0; - } - - return 1; -} diff --git a/OpenSSL/crypto/x509store.h b/OpenSSL/crypto/x509store.h deleted file mode 100644 index de3531d..0000000 --- a/OpenSSL/crypto/x509store.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * x509store.h - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * Export X509 store functions and data structures. - * See the file RATIONALE for a short explanation of why this module was written. - * - */ -#ifndef PyOpenSSL_SSL_X509STORE_H_ -#define PyOpenSSL_SSL_X509STORE_H_ - -#include -#include - -extern int init_crypto_x509store (PyObject *); - -extern PyTypeObject crypto_X509Store_Type; - -#define crypto_X509Store_Check(v) ((v)->ob_type == &crypto_X509Store_Type) - -typedef struct { - PyObject_HEAD - X509_STORE *x509_store; - int dealloc; -} crypto_X509StoreObj; - - -#endif diff --git a/OpenSSL/py3k.h b/OpenSSL/py3k.h deleted file mode 100644 index 29da2f1..0000000 --- a/OpenSSL/py3k.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef PyOpenSSL_PY3K_H_ -#define PyOpenSSL_PY3K_H_ - -#if (PY_VERSION_HEX >= 0x03000000) - -#define PY3 - -#define PyOpenSSL_MODINIT(name) \ -PyMODINIT_FUNC \ -PyInit_##name(void) - -#define PyText_CheckExact PyUnicode_CheckExact -#define PyText_FromString PyUnicode_FromString -#define PyText_FromStringAndSize PyUnicode_FromStringAndSize - -#define PyOpenSSL_HEAD_INIT(type, size) PyVarObject_HEAD_INIT(NULL, size) - -#define PyOpenSSL_Integer_Check(o) PyLong_Check(o) - -#define PyOpenSSL_MODRETURN(module) { return module; } - -#define BYTESTRING_FMT "y" - -#else /* (PY_VERSION_HEX >= 0x03000000) */ - -#define PyOpenSSL_MODRETURN(module) { return; } - -#define PyOpenSSL_HEAD_INIT(type, size) PyObject_HEAD_INIT(NULL) 0, - -#define PyBytes_FromStringAndSize PyString_FromStringAndSize - -#define PyOpenSSL_Integer_Check(o) (PyInt_Check(o) || PyLong_Check(o)) - -#define PyBytes_Size PyString_Size -#define PyBytes_Check PyString_Check -#define PyBytes_CheckExact PyString_CheckExact -#define PyBytes_AsString PyString_AsString -#define PyBytes_FromString PyString_FromString -#define PyBytes_FromStringAndSize PyString_FromStringAndSize -#define _PyBytes_Resize _PyString_Resize - -#define PyText_CheckExact PyString_CheckExact -#define PyText_FromString PyString_FromString -#define PyText_FromStringAndSize PyString_FromStringAndSize - -#define PyOpenSSL_MODINIT(name) \ -void \ -init##name(void) - -#define BYTESTRING_FMT "s" - -#endif /* (PY_VERSION_HEX >= 0x03000000) */ - -#endif /* PyOpenSSL_PY3K_H_ */ - diff --git a/OpenSSL/pymemcompat.h b/OpenSSL/pymemcompat.h deleted file mode 100644 index 24221ec..0000000 --- a/OpenSSL/pymemcompat.h +++ /dev/null @@ -1,86 +0,0 @@ -/* The idea of this file is that you bundle it with your extension, - #include it, program to Python 2.3's memory API and have your - extension build with any version of Python from 1.5.2 through to - 2.3 (and hopefully beyond). */ - -#ifndef Py_PYMEMCOMPAT_H -#define Py_PYMEMCOMPAT_H - -#include "Python.h" - -/* There are three "families" of memory API: the "raw memory", "object - memory" and "object" families. (This is ignoring the matter of the - cycle collector, about which more is said below). - - Raw Memory: - - PyMem_Malloc, PyMem_Realloc, PyMem_Free - - Object Memory: - - PyObject_Malloc, PyObject_Realloc, PyObject_Free - - Object: - - PyObject_New, PyObject_NewVar, PyObject_Del - - The raw memory and object memory allocators both mimic the - malloc/realloc/free interface from ANSI C, but the object memory - allocator can (and, since 2.3, does by default) use a different - allocation strategy biased towards lots of lots of "small" - allocations. - - The object family is used for allocating Python objects, and the - initializers take care of some basic initialization (setting the - refcount to 1 and filling out the ob_type field) as well as having - a somewhat different interface. - - Do not mix the families! E.g. do not allocate memory with - PyMem_Malloc and free it with PyObject_Free. You may get away with - it quite a lot of the time, but there *are* scenarios where this - will break. You Have Been Warned. - - Also, in many versions of Python there are an insane amount of - memory interfaces to choose from. Use the ones described above. */ - -#if PY_VERSION_HEX < 0x01060000 -/* raw memory interface already present */ - -/* there is no object memory interface in 1.5.2 */ -#define PyObject_Malloc PyMem_Malloc -#define PyObject_Realloc PyMem_Realloc -#define PyObject_Free PyMem_Free - -/* the object interface is there, but the names have changed */ -#define PyObject_New PyObject_NEW -#define PyObject_NewVar PyObject_NEW_VAR -#define PyObject_Del PyMem_Free -#endif - -/* If your object is a container you probably want to support the - cycle collector, which was new in Python 2.0. - - Unfortunately, the interface to the collector that was present in - Python 2.0 and 2.1 proved to be tricky to use, and so changed in - 2.2 -- in a way that can't easily be papered over with macros. - - This file contains macros that let you program to the 2.2 GC API. - Your module will compile against any Python since version 1.5.2, - but the type will only participate in the GC in versions 2.2 and - up. Some work is still necessary on your part to only fill out the - tp_traverse and tp_clear fields when they exist and set tp_flags - appropriately. - - It is possible to support both the 2.0 and 2.2 GC APIs, but it's - not pretty and this comment block is too narrow to contain a - desciption of what's required... */ - -#if PY_VERSION_HEX < 0x020200B1 -#define PyObject_GC_New PyObject_New -#define PyObject_GC_NewVar PyObject_NewVar -#define PyObject_GC_Del PyObject_Del -#define PyObject_GC_Track(op) -#define PyObject_GC_UnTrack(op) -#endif - -#endif /* !Py_PYMEMCOMPAT_H */ diff --git a/OpenSSL/rand.py b/OpenSSL/rand.py new file mode 100644 index 0000000..e754378 --- /dev/null +++ b/OpenSSL/rand.py @@ -0,0 +1,180 @@ +""" +PRNG management routines, thin wrappers. + +See the file RATIONALE for a short explanation of why this module was written. +""" + +from functools import partial + +from six import integer_types as _integer_types + +from OpenSSL._util import ( + ffi as _ffi, + lib as _lib, + exception_from_error_queue as _exception_from_error_queue) + + +class Error(Exception): + """ + An error occurred in an `OpenSSL.rand` API. + """ + +_raise_current_error = partial(_exception_from_error_queue, Error) + +_unspecified = object() + +_builtin_bytes = bytes + +def bytes(num_bytes): + """ + Get some random bytes as a string. + + :param num_bytes: The number of bytes to fetch + :return: A string of random bytes + """ + if not isinstance(num_bytes, _integer_types): + raise TypeError("num_bytes must be an integer") + + if num_bytes < 0: + raise ValueError("num_bytes must not be negative") + + result_buffer = _ffi.new("char[]", num_bytes) + result_code = _lib.RAND_bytes(result_buffer, num_bytes) + if result_code == -1: + # TODO: No tests for this code path. Triggering a RAND_bytes failure + # might involve supplying a custom ENGINE? That's hard. + _raise_current_error() + + return _ffi.buffer(result_buffer)[:] + + + +def add(buffer, entropy): + """ + Add data with a given entropy to the PRNG + + :param buffer: Buffer with random data + :param entropy: The entropy (in bytes) measurement of the buffer + :return: None + """ + if not isinstance(buffer, _builtin_bytes): + raise TypeError("buffer must be a byte string") + + if not isinstance(entropy, int): + raise TypeError("entropy must be an integer") + + # TODO Nothing tests this call actually being made, or made properly. + _lib.RAND_add(buffer, len(buffer), entropy) + + + +def seed(buffer): + """ + Alias for rand_add, with entropy equal to length + + :param buffer: Buffer with random data + :return: None + """ + if not isinstance(buffer, _builtin_bytes): + raise TypeError("buffer must be a byte string") + + # TODO Nothing tests this call actually being made, or made properly. + _lib.RAND_seed(buffer, len(buffer)) + + + +def status(): + """ + Retrieve the status of the PRNG + + :return: True if the PRNG is seeded enough, false otherwise + """ + return _lib.RAND_status() + + + +def egd(path, bytes=_unspecified): + """ + Query an entropy gathering daemon (EGD) for random data and add it to the + PRNG. I haven't found any problems when the socket is missing, the function + just returns 0. + + :param path: The path to the EGD socket + :param bytes: (optional) The number of bytes to read, default is 255 + :returns: The number of bytes read (NB: a value of 0 isn't necessarily an + error, check rand.status()) + """ + if not isinstance(path, _builtin_bytes): + raise TypeError("path must be a byte string") + + if bytes is _unspecified: + bytes = 255 + elif not isinstance(bytes, int): + raise TypeError("bytes must be an integer") + + return _lib.RAND_egd_bytes(path, bytes) + + + +def cleanup(): + """ + Erase the memory used by the PRNG. + + :return: None + """ + # TODO Nothing tests this call actually being made, or made properly. + _lib.RAND_cleanup() + + + +def load_file(filename, maxbytes=_unspecified): + """ + Seed the PRNG with data from a file + + :param filename: The file to read data from + :param maxbytes: (optional) The number of bytes to read, default is + to read the entire file + :return: The number of bytes read + """ + if not isinstance(filename, _builtin_bytes): + raise TypeError("filename must be a string") + + if maxbytes is _unspecified: + maxbytes = -1 + elif not isinstance(maxbytes, int): + raise TypeError("maxbytes must be an integer") + + return _lib.RAND_load_file(filename, maxbytes) + + + +def write_file(filename): + """ + Save PRNG state to a file + + :param filename: The file to write data to + :return: The number of bytes written + """ + if not isinstance(filename, _builtin_bytes): + raise TypeError("filename must be a string") + + return _lib.RAND_write_file(filename) + + +# TODO There are no tests for screen at all +def screen(): + """ + Add the current contents of the screen to the PRNG state. Availability: + Windows. + + :return: None + """ + _lib.RAND_screen() + +if getattr(_lib, 'RAND_screen', None) is None: + del screen + + +# TODO There are no tests for the RAND strings being loaded, whatever that +# means. +_lib.ERR_load_RAND_strings() diff --git a/OpenSSL/rand/rand.c b/OpenSSL/rand/rand.c deleted file mode 100644 index 8307ac6..0000000 --- a/OpenSSL/rand/rand.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * rand.c - * - * Copyright (C) AB Strakt - * See LICENSE file for details. - * - * PRNG management routines, thin wrappers. - * See the file RATIONALE for a short explanation of why this module was written. - * - */ -#include - -/* - * In order to get the RAND_screen definition from the rand.h - * WIN32 or WINDOWS needs to be defined, otherwise we get a - * warning. - */ -#ifdef MS_WINDOWS -# ifndef WIN32 -# define WIN32 -# endif -#endif -#include -#include "../util.h" - -PyObject *rand_Error; - -static char rand_doc[] = "\n\ -PRNG management routines, thin wrappers.\n\ -See the file RATIONALE for a short explanation of why this module was written.\n\ -"; - -static char rand_add_doc[] = "\n\ -Add data with a given entropy to the PRNG\n\ -\n\ -@param buffer: Buffer with random data\n\ -@param entropy: The entropy (in bytes) measurement of the buffer\n\ -@return: None\n\ -"; - -static PyObject * -rand_add(PyObject *spam, PyObject *args) -{ - char *buf; - int size; - double entropy; - - if (!PyArg_ParseTuple(args, BYTESTRING_FMT "#d:add", &buf, &size, &entropy)) - return NULL; - - RAND_add(buf, size, entropy); - - Py_INCREF(Py_None); - return Py_None; -} - -static char rand_seed_doc[] = "\n\ -Alias for rand_add, with entropy equal to length\n\ -\n\ -@param buffer: Buffer with random data\n\ -@return: None\n\ -"; - -static PyObject * -rand_seed(PyObject *spam, PyObject *args) -{ - char *buf; - int size; - - if (!PyArg_ParseTuple(args, BYTESTRING_FMT "#:seed", &buf, &size)) - return NULL; - - RAND_seed(buf, size); - - Py_INCREF(Py_None); - return Py_None; -} - -static char rand_status_doc[] = "\n\ -Retrieve the status of the PRNG\n\ -\n\ -@return: True if the PRNG is seeded enough, false otherwise\n\ -"; - -static PyObject * -rand_status(PyObject *spam, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":status")) - return NULL; - - return PyLong_FromLong((long)RAND_status()); -} - -#ifdef MS_WINDOWS -static char rand_screen_doc[] = "\n\ -Add the current contents of the screen to the PRNG state. Availability:\n\ -Windows.\n\ -\n\ -@return: None\n\ -"; - -static PyObject * -rand_screen(PyObject *spam, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":screen")) - return NULL; - - RAND_screen(); - Py_INCREF(Py_None); - return Py_None; -} -#endif - -static char rand_egd_doc[] = "\n\ -Query an entropy gathering daemon (EGD) for random data and add it to the\n\ -PRNG. I haven't found any problems when the socket is missing, the function\n\ -just returns 0.\n\ -\n\ -@param path: The path to the EGD socket\n\ -@param bytes: (optional) The number of bytes to read, default is 255\n\ -@returns: The number of bytes read (NB: a value of 0 isn't necessarily an\n\ - error, check rand.status())\n\ -"; - -static PyObject * -rand_egd(PyObject *spam, PyObject *args) -{ - char *path; - int bytes = 255; - - if (!PyArg_ParseTuple(args, "s|i:egd", &path, &bytes)) - return NULL; - - return PyLong_FromLong((long)RAND_egd_bytes(path, bytes)); -} - -static char rand_cleanup_doc[] = "\n\ -Erase the memory used by the PRNG.\n\ -\n\ -@return: None\n\ -"; - -static PyObject * -rand_cleanup(PyObject *spam, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":cleanup")) - return NULL; - - RAND_cleanup(); - - Py_INCREF(Py_None); - return Py_None; -} - -static char rand_load_file_doc[] = "\n\ -Seed the PRNG with data from a file\n\ -\n\ -@param filename: The file to read data from\n\ -@param maxbytes: (optional) The number of bytes to read, default is\n\ - to read the entire file\n\ -@return: The number of bytes read\n\ -"; - -static PyObject * -rand_load_file(PyObject *spam, PyObject *args) -{ - char *filename; - int maxbytes = -1; - - if (!PyArg_ParseTuple(args, "s|i:load_file", &filename, &maxbytes)) - return NULL; - - return PyLong_FromLong((long)RAND_load_file(filename, maxbytes)); -} - -static char rand_write_file_doc[] = "\n\ -Save PRNG state to a file\n\ -\n\ -@param filename: The file to write data to\n\ -@return: The number of bytes written\n\ -"; - -static PyObject * -rand_write_file(PyObject *spam, PyObject *args) -{ - char *filename; - - if (!PyArg_ParseTuple(args, "s:write_file", &filename)) - return NULL; - - return PyLong_FromLong((long)RAND_write_file(filename)); -} - -static char rand_bytes_doc[] = "\n\ -Get some randomm bytes as a string.\n\ -\n\ -@param num_bytes: The number of bytes to fetch\n\ -@return: A string of random bytes\n\ -"; - -#if PY_VERSION_HEX < 0x02050000 -#define Py_ssize_t int -#define PY_SSIZE_FMT "i" -#else -#define PY_SSIZE_FMT "n" -#endif - -static PyObject * -rand_bytes(PyObject *spam, PyObject *args, PyObject *keywds) { - Py_ssize_t num_bytes; - static char *kwlist[] = {"num_bytes", NULL}; - char *buf; - unsigned int rc; - PyObject *obj = NULL; - - if (!PyArg_ParseTupleAndKeywords( - args, keywds, PY_SSIZE_FMT ":bytes", kwlist, &num_bytes)) { - return NULL; - } - - if(num_bytes < 0) { - PyErr_SetString(PyExc_ValueError, "num_bytes must not be negative"); - return NULL; - } - buf = malloc(num_bytes); - if (buf == NULL) /* out of memory */ - return NULL; - rc = RAND_bytes((unsigned char *) buf, num_bytes); - if(rc != 1) { /* if unsuccessful */ - exception_from_error_queue(rand_Error); - goto done; - } - obj = PyBytes_FromStringAndSize(buf, (unsigned) num_bytes); - done: - free(buf); - return obj; -} - - -/* Methods in the OpenSSL.rand module */ -static PyMethodDef rand_methods[] = { - { "add", (PyCFunction)rand_add, METH_VARARGS, rand_add_doc }, - { "seed", (PyCFunction)rand_seed, METH_VARARGS, rand_seed_doc }, - { "status", (PyCFunction)rand_status, METH_VARARGS, rand_status_doc }, -#ifdef MS_WINDOWS - { "screen", (PyCFunction)rand_screen, METH_VARARGS, rand_screen_doc }, -#endif - { "egd", (PyCFunction)rand_egd, METH_VARARGS, rand_egd_doc }, - { "cleanup", (PyCFunction)rand_cleanup, METH_VARARGS, rand_cleanup_doc }, - { "load_file", (PyCFunction)rand_load_file, METH_VARARGS, rand_load_file_doc }, - { "write_file",(PyCFunction)rand_write_file, METH_VARARGS, rand_write_file_doc }, - { "bytes", (PyCFunction)rand_bytes, METH_VARARGS|METH_KEYWORDS, rand_bytes_doc }, - { NULL, NULL } -}; - - -#ifdef PY3 -static struct PyModuleDef randmodule = { - PyModuleDef_HEAD_INIT, - "rand", - rand_doc, - -1, - rand_methods -}; -#endif - -/* - * Initialize the rand sub module - * - * Arguments: None - * Returns: None - */ -PyOpenSSL_MODINIT(rand) { - PyObject *module; - -#ifdef PY3 - module = PyModule_Create(&randmodule); -#else - module = Py_InitModule3("rand", rand_methods, rand_doc); -#endif - if (module == NULL) { - PyOpenSSL_MODRETURN(NULL); - } - - rand_Error = PyErr_NewException("OpenSSL.rand.Error", NULL, NULL); - - if (rand_Error == NULL) { - goto error; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF(rand_Error); - if (PyModule_AddObject(module, "Error", rand_Error) != 0) { - goto error; - } - - ERR_load_RAND_strings(); - - PyOpenSSL_MODRETURN(module); - -error: - PyOpenSSL_MODRETURN(NULL); - ; -} - diff --git a/OpenSSL/ssl/connection.c b/OpenSSL/ssl/connection.c deleted file mode 100755 index 9e9794b..0000000 --- a/OpenSSL/ssl/connection.c +++ /dev/null @@ -1,1581 +0,0 @@ -/* - * connection.c - * - * Copyright (C) AB Strakt - * Copyright (C) Jean-Paul Calderone - * See LICENSE for details. - * - * SSL Connection objects and methods. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - */ -#include - -#ifndef MS_WINDOWS -# include -# include -# if !(defined(__BEOS__) || defined(__CYGWIN__)) -# include -# endif -#else -# include -# include -#endif - -#define SSL_MODULE -#include -#include -#include "ssl.h" - -/** - * If we are on UNIX, fine, just use PyErr_SetFromErrno. If we are on Windows, - * apply some black winsock voodoo. This is basically just copied from Python's - * socketmodule.c - * - * Arguments: None - * Returns: None - */ -static void -syscall_from_errno(void) -{ -#ifdef MS_WINDOWS - int errnum = WSAGetLastError(); - if (errnum) - { - static struct { int num; const char *msg; } *msgp, msgs[] = { - { WSAEINTR, "Interrupted system call" }, - { WSAEBADF, "Bad file descriptor" }, - { WSAEACCES, "Permission denied" }, - { WSAEFAULT, "Bad address" }, - { WSAEINVAL, "Invalid argument" }, - { WSAEMFILE, "Too many open files" }, - { WSAEWOULDBLOCK, "The socket operation could not complete " - "without blocking" }, - { WSAEINPROGRESS, "Operation now in progress" }, - { WSAEALREADY, "Operation already in progress" }, - { WSAENOTSOCK, "Socket operation on non-socket" }, - { WSAEDESTADDRREQ, "Destination address required" }, - { WSAEMSGSIZE, "Message too long" }, - { WSAEPROTOTYPE, "Protocol wrong type for socket" }, - { WSAENOPROTOOPT, "Protocol not available" }, - { WSAEPROTONOSUPPORT, "Protocol not supported" }, - { WSAESOCKTNOSUPPORT, "Socket type not supported" }, - { WSAEOPNOTSUPP, "Operation not supported" }, - { WSAEPFNOSUPPORT, "Protocol family not supported" }, - { WSAEAFNOSUPPORT, "Address family not supported" }, - { WSAEADDRINUSE, "Address already in use" }, - { WSAEADDRNOTAVAIL, "Can't assign requested address" }, - { WSAENETDOWN, "Network is down" }, - { WSAENETUNREACH, "Network is unreachable" }, - { WSAENETRESET, "Network dropped connection on reset" }, - { WSAECONNABORTED, "Software caused connection abort" }, - { WSAECONNRESET, "Connection reset by peer" }, - { WSAENOBUFS, "No buffer space available" }, - { WSAEISCONN, "Socket is already connected" }, - { WSAENOTCONN, "Socket is not connected" }, - { WSAESHUTDOWN, "Can't send after socket shutdown" }, - { WSAETOOMANYREFS, "Too many references: can't splice" }, - { WSAETIMEDOUT, "Operation timed out" }, - { WSAECONNREFUSED, "Connection refused" }, - { WSAELOOP, "Too many levels of symbolic links" }, - { WSAENAMETOOLONG, "File name too long" }, - { WSAEHOSTDOWN, "Host is down" }, - { WSAEHOSTUNREACH, "No route to host" }, - { WSAENOTEMPTY, "Directory not empty" }, - { WSAEPROCLIM, "Too many processes" }, - { WSAEUSERS, "Too many users" }, - { WSAEDQUOT, "Disc quota exceeded" }, - { WSAESTALE, "Stale NFS file handle" }, - { WSAEREMOTE, "Too many levels of remote in path" }, - { WSASYSNOTREADY, "Network subsystem is unvailable" }, - { WSAVERNOTSUPPORTED, "WinSock version is not supported" }, - { WSANOTINITIALISED, "Successful WSAStartup() not yet performed" }, - { WSAEDISCON, "Graceful shutdown in progress" }, - /* Resolver errors */ - { WSAHOST_NOT_FOUND, "No such host is known" }, - { WSATRY_AGAIN, "Host not found, or server failed" }, - { WSANO_RECOVERY, "Unexpected server error encountered" }, - { WSANO_DATA, "Valid name without requested data" }, - { WSANO_ADDRESS, "No address, look for MX record" }, - { 0, NULL } - }; - PyObject *v; - const char *msg = "winsock error"; - - for (msgp = msgs; msgp->msg; msgp++) - { - if (errnum == msgp->num) - { - msg = msgp->msg; - break; - } - } - - v = Py_BuildValue("(is)", errnum, msg); - if (v != NULL) - { - PyErr_SetObject(ssl_SysCallError, v); - Py_DECREF(v); - } - return; - } -#else - PyErr_SetFromErrno(ssl_SysCallError); -#endif -} - -/* - * Handle errors raised by BIO functions. - * - * Arguments: bio - The BIO object - * ret - The return value of the BIO_ function. - * Returns: None, the calling function should return NULL; - */ -static void -handle_bio_errors(BIO* bio, int ret) -{ - if (BIO_should_retry(bio)) { - if (BIO_should_read(bio)) { - PyErr_SetNone(ssl_WantReadError); - } else if (BIO_should_write(bio)) { - PyErr_SetNone(ssl_WantWriteError); - } else if (BIO_should_io_special(bio)) { - /* - * It's somewhat unclear what this means. From the OpenSSL source, - * it seems like it should not be triggered by the memory BIO, so - * for the time being, this case shouldn't come up. The SSL BIO - * (which I think should be named the socket BIO) may trigger this - * case if its socket is not yet connected or it is busy doing - * something related to x509. - */ - PyErr_SetString(PyExc_ValueError, "BIO_should_io_special"); - } else { - /* - * I hope this is dead code. The BIO documentation suggests that - * one of the above three checks should always be true. - */ - PyErr_SetString(PyExc_ValueError, "unknown bio failure"); - } - } else { - /* - * If we aren't to retry, it's really an error, so fall back to the - * normal error reporting code. However, the BIO interface does not - * specify a uniform error reporting mechanism. We can only hope that - * the code which triggered the error also kindly pushed something onto - * the error stack. - */ - exception_from_error_queue(ssl_Error); - } -} - -/* - * Handle errors raised by SSL I/O functions. NOTE: Not SSL_shutdown ;) - * - * Arguments: ssl - The SSL object - * err - The return code from SSL_get_error - * ret - The return code from the SSL I/O function - * Returns: None, the calling function should return NULL - */ -static void -handle_ssl_errors(SSL *ssl, int err, int ret) -{ - switch (err) - { - /* - * Strange as it may seem, ZeroReturn is not an error per se. It means - * that the SSL Connection has been closed correctly (note, not the - * transport layer!), i.e. closure alerts have been exchanged. This is - * an exception since - * + There's an SSL "error" code for it - * + You have to deal with it in any case, close the transport layer - * etc - */ - case SSL_ERROR_ZERO_RETURN: - PyErr_SetNone(ssl_ZeroReturnError); - break; - - /* - * The WantXYZ exceptions don't mean that there's an error, just that - * nothing could be read/written just now, maybe because the transport - * layer would block on the operation, or that there's not enough data - * available to fill an entire SSL record. - */ - case SSL_ERROR_WANT_READ: - PyErr_SetNone(ssl_WantReadError); - break; - - case SSL_ERROR_WANT_WRITE: - PyErr_SetNone(ssl_WantWriteError); - break; - - case SSL_ERROR_WANT_X509_LOOKUP: - PyErr_SetNone(ssl_WantX509LookupError); - break; - - case SSL_ERROR_SYSCALL: - if (ERR_peek_error() == 0) - { - if (ret < 0) - { - syscall_from_errno(); - } - else - { - PyObject *v; - - v = Py_BuildValue("(is)", -1, "Unexpected EOF"); - if (v != NULL) - { - PyErr_SetObject(ssl_SysCallError, v); - Py_DECREF(v); - } - } - break; - } - - /* NOTE: Fall-through here, we don't want to duplicate code, right? */ - - case SSL_ERROR_SSL: - ; - default: - exception_from_error_queue(ssl_Error); - break; - } -} - -/* - * Here be member methods of the Connection "class" - */ - -static char ssl_Connection_get_context_doc[] = "\n\ -Get session context\n\ -\n\ -@return: A Context object\n\ -"; -static PyObject * -ssl_Connection_get_context(ssl_ConnectionObj *self, PyObject *args) { - if (!PyArg_ParseTuple(args, ":get_context")) { - return NULL; - } - - Py_INCREF(self->context); - return (PyObject *)self->context; -} - -static char ssl_Connection_set_context_doc[] = "\n\ -Switch this connection to a new session context\n\ -\n\ -@param context: A L{Context} instance giving the new session context to use.\n\ -\n\ -"; -static PyObject * -ssl_Connection_set_context(ssl_ConnectionObj *self, PyObject *args) { - ssl_ContextObj *ctx; - ssl_ContextObj *old; - - if (!PyArg_ParseTuple(args, "O!:set_context", &ssl_Context_Type, &ctx)) { - return NULL; - } - - /* This Connection will hold on to this context now. Make sure it stays - * alive. - */ - Py_INCREF(ctx); - - /* XXX The unit tests don't actually verify that this call is made. - * They're satisfied if self->context gets updated. - */ - SSL_set_SSL_CTX(self->ssl, ctx->ctx); - - /* Swap the old out and the new in. - */ - old = self->context; - self->context = ctx; - - /* XXX The unit tests don't verify that this reference is dropped. - */ - Py_DECREF(old); - - Py_INCREF(Py_None); - return Py_None; -} - -static char ssl_Connection_get_servername_doc[] = "\n\ -Retrieve the servername extension value if provided in the client hello\n\ -message, or None if there wasn't one.\n\ -\n\ -@return: A byte string giving the server name or C{None}.\n\ -\n\ -"; -static PyObject * -ssl_Connection_get_servername(ssl_ConnectionObj *self, PyObject *args) { - int type = TLSEXT_NAMETYPE_host_name; - const char *name; - - if (!PyArg_ParseTuple(args, ":get_servername")) { - return NULL; - } - - name = SSL_get_servername(self->ssl, type); - - if (name == NULL) { - Py_INCREF(Py_None); - return Py_None; - } else { - return PyBytes_FromString(name); - } -} - - -static char ssl_Connection_set_tlsext_host_name_doc[] = "\n\ -Set the value of the servername extension to send in the client hello.\n\ -\n\ -@param name: A byte string giving the name.\n\ -\n\ -"; -static PyObject * -ssl_Connection_set_tlsext_host_name(ssl_ConnectionObj *self, PyObject *args) { - char *buf; - - if (!PyArg_ParseTuple(args, BYTESTRING_FMT ":set_tlsext_host_name", &buf)) { - return NULL; - } - - /* XXX I guess this can fail sometimes? */ - SSL_set_tlsext_host_name(self->ssl, buf); - - Py_INCREF(Py_None); - return Py_None; -} - - - -static char ssl_Connection_pending_doc[] = "\n\ -Get the number of bytes that can be safely read from the connection\n\ -\n\ -@return: The number of bytes available in the receive buffer.\n\ -"; -static PyObject * -ssl_Connection_pending(ssl_ConnectionObj *self, PyObject *args) { - int ret; - - if (!PyArg_ParseTuple(args, ":pending")) { - return NULL; - } - - ret = SSL_pending(self->ssl); - return PyLong_FromLong((long)ret); -} - -static char ssl_Connection_bio_write_doc[] = "\n\ -When using non-socket connections this function sends\n\ -\"dirty\" data that would have traveled in on the network.\n\ -\n\ -@param buf: The string to put into the memory BIO.\n\ -@return: The number of bytes written\n\ -"; -static PyObject * -ssl_Connection_bio_write(ssl_ConnectionObj *self, PyObject *args) -{ - char *buf; - int len, ret; - - if (self->into_ssl == NULL) - { - PyErr_SetString(PyExc_TypeError, "Connection sock was not None"); - return NULL; - } - - if (!PyArg_ParseTuple(args, "s#|i:bio_write", &buf, &len)) - return NULL; - - ret = BIO_write(self->into_ssl, buf, len); - - if (PyErr_Occurred()) - { - flush_error_queue(); - return NULL; - } - - if (ret <= 0) { - /* - * There was a problem with the BIO_write of some sort. - */ - handle_bio_errors(self->into_ssl, ret); - return NULL; - } - - return PyLong_FromLong((long)ret); -} - -static char ssl_Connection_send_doc[] = "\n\ -Send data on the connection. NOTE: If you get one of the WantRead,\n\ -WantWrite or WantX509Lookup exceptions on this, you have to call the\n\ -method again with the SAME buffer.\n\ -\n\ -@param buf: The string to send\n\ -@param flags: (optional) Included for compatibility with the socket\n\ - API, the value is ignored\n\ -@return: The number of bytes written\n\ -"; -static PyObject * -ssl_Connection_send(ssl_ConnectionObj *self, PyObject *args) { - int len, ret, err, flags; - char *buf; - -#if PY_VERSION_HEX >= 0x02060000 - Py_buffer pbuf; - - if (!PyArg_ParseTuple(args, "s*|i:send", &pbuf, &flags)) - return NULL; - - buf = pbuf.buf; - len = pbuf.len; -#else - - if (!PyArg_ParseTuple(args, "s#|i:send", &buf, &len, &flags)) - return NULL; -#endif - - MY_BEGIN_ALLOW_THREADS(self->tstate) - ret = SSL_write(self->ssl, buf, len); - MY_END_ALLOW_THREADS(self->tstate) - -#if PY_VERSION_HEX >= 0x02060000 - PyBuffer_Release(&pbuf); -#endif - - if (PyErr_Occurred()) - { - flush_error_queue(); - return NULL; - } - - err = SSL_get_error(self->ssl, ret); - if (err == SSL_ERROR_NONE) - { - return PyLong_FromLong((long)ret); - } - else - { - handle_ssl_errors(self->ssl, err, ret); - return NULL; - } -} - -static char ssl_Connection_sendall_doc[] = "\n\ -Send \"all\" data on the connection. This calls send() repeatedly until\n\ -all data is sent. If an error occurs, it's impossible to tell how much data\n\ -has been sent.\n\ -\n\ -@param buf: The string to send\n\ -@param flags: (optional) Included for compatibility with the socket\n\ - API, the value is ignored\n\ -@return: The number of bytes written\n\ -"; -static PyObject * -ssl_Connection_sendall(ssl_ConnectionObj *self, PyObject *args) -{ - char *buf; - int len, ret, err, flags; - PyObject *pyret = Py_None; - -#if PY_VERSION_HEX >= 0x02060000 - Py_buffer pbuf; - - if (!PyArg_ParseTuple(args, "s*|i:sendall", &pbuf, &flags)) - return NULL; - - buf = pbuf.buf; - len = pbuf.len; -#else - if (!PyArg_ParseTuple(args, "s#|i:sendall", &buf, &len, &flags)) - return NULL; -#endif - - do { - MY_BEGIN_ALLOW_THREADS(self->tstate) - ret = SSL_write(self->ssl, buf, len); - MY_END_ALLOW_THREADS(self->tstate) - if (PyErr_Occurred()) - { - flush_error_queue(); - pyret = NULL; - break; - } - err = SSL_get_error(self->ssl, ret); - if (err == SSL_ERROR_NONE) - { - buf += ret; - len -= ret; - } - else if (err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL || - err == SSL_ERROR_ZERO_RETURN) - { - handle_ssl_errors(self->ssl, err, ret); - pyret = NULL; - break; - } - } while (len > 0); - -#if PY_VERSION_HEX >= 0x02060000 - PyBuffer_Release(&pbuf); -#endif - - Py_XINCREF(pyret); - return pyret; -} - -static char ssl_Connection_recv_doc[] = "\n\ -Receive data on the connection. NOTE: If you get one of the WantRead,\n\ -WantWrite or WantX509Lookup exceptions on this, you have to call the\n\ -method again with the SAME buffer.\n\ -\n\ -@param bufsiz: The maximum number of bytes to read\n\ -@param flags: (optional) Included for compatibility with the socket\n\ - API, the value is ignored\n\ -@return: The string read from the Connection\n\ -"; -static PyObject * -ssl_Connection_recv(ssl_ConnectionObj *self, PyObject *args) -{ - int bufsiz, ret, err, flags; - PyObject *buf; - - if (!PyArg_ParseTuple(args, "i|i:recv", &bufsiz, &flags)) - return NULL; - - buf = PyBytes_FromStringAndSize(NULL, bufsiz); - if (buf == NULL) - return NULL; - - MY_BEGIN_ALLOW_THREADS(self->tstate) - ret = SSL_read(self->ssl, PyBytes_AsString(buf), bufsiz); - MY_END_ALLOW_THREADS(self->tstate) - - if (PyErr_Occurred()) - { - Py_DECREF(buf); - flush_error_queue(); - return NULL; - } - - err = SSL_get_error(self->ssl, ret); - if (err == SSL_ERROR_NONE) - { - if (ret != bufsiz && _PyBytes_Resize(&buf, ret) < 0) - return NULL; - return buf; - } - else - { - handle_ssl_errors(self->ssl, err, ret); - Py_DECREF(buf); - return NULL; - } -} - -static char ssl_Connection_bio_read_doc[] = "\n\ -When using non-socket connections this function reads\n\ -the \"dirty\" data that would have traveled away on the network.\n\ -\n\ -@param bufsiz: The maximum number of bytes to read\n\ -@return: The string read.\n\ -"; -static PyObject * -ssl_Connection_bio_read(ssl_ConnectionObj *self, PyObject *args) -{ - int bufsiz, ret; - PyObject *buf; - - if (self->from_ssl == NULL) - { - PyErr_SetString(PyExc_TypeError, "Connection sock was not None"); - return NULL; - } - - if (!PyArg_ParseTuple(args, "i:bio_read", &bufsiz)) - return NULL; - - buf = PyBytes_FromStringAndSize(NULL, bufsiz); - if (buf == NULL) - return NULL; - - ret = BIO_read(self->from_ssl, PyBytes_AsString(buf), bufsiz); - - if (PyErr_Occurred()) - { - Py_DECREF(buf); - flush_error_queue(); - return NULL; - } - - if (ret <= 0) { - /* - * There was a problem with the BIO_read of some sort. - */ - handle_bio_errors(self->from_ssl, ret); - Py_DECREF(buf); - return NULL; - } - - /* - * Shrink the string to match the number of bytes we actually read. - */ - if (ret != bufsiz && _PyBytes_Resize(&buf, ret) < 0) - { - Py_DECREF(buf); - return NULL; - } - return buf; -} - -static char ssl_Connection_renegotiate_doc[] = "\n\ -Renegotiate the session\n\ -\n\ -@return: True if the renegotiation can be started, false otherwise\n\ -"; -static PyObject * -ssl_Connection_renegotiate(ssl_ConnectionObj *self, PyObject *args) { - int ret; - - if (!PyArg_ParseTuple(args, ":renegotiate")) { - return NULL; - } - - MY_BEGIN_ALLOW_THREADS(self->tstate); - ret = SSL_renegotiate(self->ssl); - MY_END_ALLOW_THREADS(self->tstate); - - if (PyErr_Occurred()) { - flush_error_queue(); - return NULL; - } - - return PyLong_FromLong((long)ret); -} - -static char ssl_Connection_do_handshake_doc[] = "\n\ -Perform an SSL handshake (usually called after renegotiate() or one of\n\ -set_*_state()). This can raise the same exceptions as send and recv.\n\ -\n\ -@return: None.\n\ -"; -static PyObject * -ssl_Connection_do_handshake(ssl_ConnectionObj *self, PyObject *args) -{ - int ret, err; - - if (!PyArg_ParseTuple(args, ":do_handshake")) - return NULL; - - MY_BEGIN_ALLOW_THREADS(self->tstate); - ret = SSL_do_handshake(self->ssl); - MY_END_ALLOW_THREADS(self->tstate); - - if (PyErr_Occurred()) - { - flush_error_queue(); - return NULL; - } - - err = SSL_get_error(self->ssl, ret); - if (err == SSL_ERROR_NONE) - { - Py_INCREF(Py_None); - return Py_None; - } - else - { - handle_ssl_errors(self->ssl, err, ret); - return NULL; - } -} - -#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00907000L -static char ssl_Connection_renegotiate_pending_doc[] = "\n\ -Check if there's a renegotiation in progress, it will return false once\n\ -a renegotiation is finished.\n\ -\n\ -@return: Whether there's a renegotiation in progress\n\ -"; -static PyObject * -ssl_Connection_renegotiate_pending(ssl_ConnectionObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":renegotiate_pending")) - return NULL; - - return PyLong_FromLong((long)SSL_renegotiate_pending(self->ssl)); -} -#endif - -static char ssl_Connection_total_renegotiations_doc[] = "\n\ -Find out the total number of renegotiations.\n\ -\n\ -@return: The number of renegotiations.\n\ -"; -static PyObject * -ssl_Connection_total_renegotiations(ssl_ConnectionObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":total_renegotiations")) - return NULL; - - return PyLong_FromLong(SSL_total_renegotiations(self->ssl)); -} - -static char ssl_Connection_set_accept_state_doc[] = "\n\ -Set the connection to work in server mode. The handshake will be handled\n\ -automatically by read/write.\n\ -\n\ -@return: None\n\ -"; -static PyObject * -ssl_Connection_set_accept_state(ssl_ConnectionObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":set_accept_state")) - return NULL; - - SSL_set_accept_state(self->ssl); - - Py_INCREF(Py_None); - return Py_None; -} - -static char ssl_Connection_set_connect_state_doc[] = "\n\ -Set the connection to work in client mode. The handshake will be handled\n\ -automatically by read/write.\n\ -\n\ -@return: None\n\ -"; -static PyObject * -ssl_Connection_set_connect_state(ssl_ConnectionObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":set_connect_state")) - return NULL; - - SSL_set_connect_state(self->ssl); - - Py_INCREF(Py_None); - return Py_None; -} - -static char ssl_Connection_connect_doc[] = "\n\ -Connect to remote host and set up client-side SSL\n\ -\n\ -@param addr: A remote address\n\ -@return: What the socket's connect method returns\n\ -"; -static PyObject * -ssl_Connection_connect(ssl_ConnectionObj *self, PyObject *args) -{ - PyObject *meth, *ret; - - if ((meth = PyObject_GetAttrString(self->socket, "connect")) == NULL) - return NULL; - - SSL_set_connect_state(self->ssl); - - ret = PyEval_CallObject(meth, args); - Py_DECREF(meth); - if (ret == NULL) - return NULL; - - return ret; -} - -static char ssl_Connection_connect_ex_doc[] = "\n\ -Connect to remote host and set up client-side SSL. Note that if the socket's\n\ -connect_ex method doesn't return 0, SSL won't be initialized.\n\ -\n\ -@param addr: A remove address\n\ -@return: What the socket's connect_ex method returns\n\ -"; -static PyObject * -ssl_Connection_connect_ex(ssl_ConnectionObj *self, PyObject *args) -{ - PyObject *meth, *ret; - - if ((meth = PyObject_GetAttrString(self->socket, "connect_ex")) == NULL) - return NULL; - - SSL_set_connect_state(self->ssl); - - ret = PyEval_CallObject(meth, args); - Py_DECREF(meth); - return ret; -} - -static char ssl_Connection_accept_doc[] = "\n\ -Accept incoming connection and set up SSL on it\n\ -\n\ -@return: A (conn,addr) pair where conn is a Connection and addr is an\n\ - address\n\ -"; -static PyObject * -ssl_Connection_accept(ssl_ConnectionObj *self, PyObject *args) -{ - PyObject *tuple, *socket, *address, *meth; - ssl_ConnectionObj *conn; - - if ((meth = PyObject_GetAttrString(self->socket, "accept")) == NULL) - return NULL; - tuple = PyEval_CallObject(meth, args); - Py_DECREF(meth); - if (tuple == NULL) - return NULL; - - socket = PyTuple_GetItem(tuple, 0); - Py_INCREF(socket); - address = PyTuple_GetItem(tuple, 1); - Py_INCREF(address); - Py_DECREF(tuple); - - conn = ssl_Connection_New(self->context, socket); - Py_DECREF(socket); - if (conn == NULL) - { - Py_DECREF(address); - return NULL; - } - - SSL_set_accept_state(conn->ssl); - - tuple = Py_BuildValue("(OO)", conn, address); - - Py_DECREF(conn); - Py_DECREF(address); - - return tuple; -} - -static char ssl_Connection_bio_shutdown_doc[] = "\n\ -When using non-socket connections this function signals end of\n\ -data on the input for this connection.\n\ -\n\ -@return: None\n\ -"; - -static PyObject * -ssl_Connection_bio_shutdown(ssl_ConnectionObj *self, PyObject *args) -{ - if (self->from_ssl == NULL) - { - PyErr_SetString(PyExc_TypeError, "Connection sock was not None"); - return NULL; - } - - BIO_set_mem_eof_return(self->into_ssl, 0); - Py_INCREF(Py_None); - return Py_None; -} - - - -static char ssl_Connection_shutdown_doc[] = "\n\ -Send closure alert\n\ -\n\ -@return: True if the shutdown completed successfully (i.e. both sides\n\ - have sent closure alerts), false otherwise (i.e. you have to\n\ - wait for a ZeroReturnError on a recv() method call\n\ -"; -static PyObject * -ssl_Connection_shutdown(ssl_ConnectionObj *self, PyObject *args) -{ - int ret; - - if (!PyArg_ParseTuple(args, ":shutdown")) - return NULL; - - MY_BEGIN_ALLOW_THREADS(self->tstate) - ret = SSL_shutdown(self->ssl); - MY_END_ALLOW_THREADS(self->tstate) - - if (PyErr_Occurred()) - { - flush_error_queue(); - return NULL; - } - - if (ret < 0) - { - exception_from_error_queue(ssl_Error); - return NULL; - } - else if (ret > 0) - { - Py_INCREF(Py_True); - return Py_True; - } - else - { - Py_INCREF(Py_False); - return Py_False; - } -} - -static char ssl_Connection_get_cipher_list_doc[] = "\n\ -Get the session cipher list\n\ -\n\ -@return: A list of cipher strings\n\ -"; -static PyObject * -ssl_Connection_get_cipher_list(ssl_ConnectionObj *self, PyObject *args) -{ - int idx = 0; - const char *ret; - PyObject *lst, *item; - - if (!PyArg_ParseTuple(args, ":get_cipher_list")) - return NULL; - - lst = PyList_New(0); - while ((ret = SSL_get_cipher_list(self->ssl, idx)) != NULL) - { - item = PyText_FromString(ret); - PyList_Append(lst, item); - Py_DECREF(item); - idx++; - } - return lst; -} - -static char ssl_Connection_get_client_ca_list_doc[] = "\n\ -Get CAs whose certificates are suggested for client authentication.\n\ -\n\ -@return: If this is a server connection, a list of X509Names representing\n\ - the acceptable CAs as set by L{OpenSSL.SSL.Context.set_client_ca_list} or\n\ - L{OpenSSL.SSL.Context.add_client_ca}. If this is a client connection,\n\ - the list of such X509Names sent by the server, or an empty list if that\n\ - has not yet happened.\n\ -"; - -static PyObject * -ssl_Connection_get_client_ca_list(ssl_ConnectionObj *self, PyObject *args) { - STACK_OF(X509_NAME) *CANames; - PyObject *CAList; - int i, n; - - if (!PyArg_ParseTuple(args, ":get_client_ca_list")) { - return NULL; - } - CANames = SSL_get_client_CA_list(self->ssl); - if (CANames == NULL) { - return PyList_New(0); - } - n = sk_X509_NAME_num(CANames); - CAList = PyList_New(n); - if (CAList == NULL) { - return NULL; - } - for (i = 0; i < n; i++) { - X509_NAME *CAName; - PyObject *CA; - - CAName = X509_NAME_dup(sk_X509_NAME_value(CANames, i)); - if (CAName == NULL) { - Py_DECREF(CAList); - exception_from_error_queue(ssl_Error); - return NULL; - } - CA = (PyObject *)new_x509name(CAName, 1); - if (CA == NULL) { - X509_NAME_free(CAName); - Py_DECREF(CAList); - return NULL; - } - if (PyList_SetItem(CAList, i, CA)) { - Py_DECREF(CA); - Py_DECREF(CAList); - return NULL; - } - } - return CAList; -} - -static char ssl_Connection_makefile_doc[] = "\n\ -The makefile() method is not implemented, since there is no dup semantics\n\ -for SSL connections\n\ -\n\ -@raise NotImplementedError\n\ -"; -static PyObject * -ssl_Connection_makefile(ssl_ConnectionObj *self, PyObject *args) -{ - PyErr_SetString(PyExc_NotImplementedError, "Cannot make file object of SSL.Connection"); - return NULL; -} - -static char ssl_Connection_get_app_data_doc[] = "\n\ -Get application data\n\ -\n\ -@return: The application data\n\ -"; -static PyObject * -ssl_Connection_get_app_data(ssl_ConnectionObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":get_app_data")) - return NULL; - - Py_INCREF(self->app_data); - return self->app_data; -} - -static char ssl_Connection_set_app_data_doc[] = "\n\ -Set application data\n\ -\n\ -@param data - The application data\n\ -@return: None\n\ -"; -static PyObject * -ssl_Connection_set_app_data(ssl_ConnectionObj *self, PyObject *args) -{ - PyObject *data; - - if (!PyArg_ParseTuple(args, "O:set_app_data", &data)) - return NULL; - - Py_DECREF(self->app_data); - Py_INCREF(data); - self->app_data = data; - - Py_INCREF(Py_None); - return Py_None; -} - -static char ssl_Connection_get_shutdown_doc[] = "\n\ -Get shutdown state\n\ -\n\ -@return: The shutdown state, a bitvector of SENT_SHUTDOWN, RECEIVED_SHUTDOWN.\n\ -"; -static PyObject * -ssl_Connection_get_shutdown(ssl_ConnectionObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":get_shutdown")) - return NULL; - - return PyLong_FromLong((long)SSL_get_shutdown(self->ssl)); -} - -static char ssl_Connection_set_shutdown_doc[] = "\n\ -Set shutdown state\n\ -\n\ -@param state - bitvector of SENT_SHUTDOWN, RECEIVED_SHUTDOWN.\n\ -@return: None\n\ -"; -static PyObject * -ssl_Connection_set_shutdown(ssl_ConnectionObj *self, PyObject *args) -{ - int shutdown; - - if (!PyArg_ParseTuple(args, "i:set_shutdown", &shutdown)) - return NULL; - - SSL_set_shutdown(self->ssl, shutdown); - Py_INCREF(Py_None); - return Py_None; -} - -static char ssl_Connection_state_string_doc[] = "\n\ -Get a verbose state description\n\ -\n\ -@return: A string representing the state\n\ -"; -static PyObject * -ssl_Connection_state_string(ssl_ConnectionObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":state_string")) - return NULL; - - return PyText_FromString(SSL_state_string_long(self->ssl)); -} - -static char ssl_Connection_client_random_doc[] = "\n\ -Get a copy of the client hello nonce.\n\ -\n\ -@return: A string representing the state\n\ -"; -static PyObject * -ssl_Connection_client_random(ssl_ConnectionObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":client_random")) - return NULL; - - if (self->ssl->session == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - return PyBytes_FromStringAndSize( (const char *) self->ssl->s3->client_random, SSL3_RANDOM_SIZE); -} - -static char ssl_Connection_server_random_doc[] = "\n\ -Get a copy of the server hello nonce.\n\ -\n\ -@return: A string representing the state\n\ -"; -static PyObject * -ssl_Connection_server_random(ssl_ConnectionObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":server_random")) - return NULL; - - if (self->ssl->session == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - return PyBytes_FromStringAndSize( (const char *) self->ssl->s3->server_random, SSL3_RANDOM_SIZE); -} - -static char ssl_Connection_master_key_doc[] = "\n\ -Get a copy of the master key.\n\ -\n\ -@return: A string representing the state\n\ -"; -static PyObject * -ssl_Connection_master_key(ssl_ConnectionObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":master_key")) - return NULL; - - if (self->ssl->session == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - return PyBytes_FromStringAndSize( (const char *) self->ssl->session->master_key, self->ssl->session->master_key_length); -} - -static char ssl_Connection_sock_shutdown_doc[] = "\n\ -See shutdown(2)\n\ -\n\ -@return: What the socket's shutdown() method returns\n\ -"; -static PyObject * -ssl_Connection_sock_shutdown(ssl_ConnectionObj *self, PyObject *args) -{ - PyObject *meth, *ret; - - if ((meth = PyObject_GetAttrString(self->socket, "shutdown")) == NULL) - return NULL; - ret = PyEval_CallObject(meth, args); - Py_DECREF(meth); - return ret; -} - -static char ssl_Connection_get_peer_certificate_doc[] = "\n\ -Retrieve the other side's certificate (if any)\n\ -\n\ -@return: The peer's certificate\n\ -"; -static PyObject * -ssl_Connection_get_peer_certificate(ssl_ConnectionObj *self, PyObject *args) -{ - X509 *cert; - - if (!PyArg_ParseTuple(args, ":get_peer_certificate")) - return NULL; - - cert = SSL_get_peer_certificate(self->ssl); - if (cert != NULL) - { - return (PyObject *)new_x509(cert, 1); - } - else - { - Py_INCREF(Py_None); - return Py_None; - } -} - -static char ssl_Connection_get_peer_cert_chain_doc[] = "\n\ -Retrieve the other side's certificate (if any)\n\ -\n\ -@return: A list of X509 instances giving the peer's certificate chain,\n\ - or None if it does not have one.\n\ -"; -static PyObject * -ssl_Connection_get_peer_cert_chain(ssl_ConnectionObj *self, PyObject *args) { - STACK_OF(X509) *sk; - PyObject *chain; - crypto_X509Obj *cert; - Py_ssize_t i; - - if (!PyArg_ParseTuple(args, ":get_peer_cert_chain")) { - return NULL; - } - - sk = SSL_get_peer_cert_chain(self->ssl); - if (sk != NULL) { - chain = PyList_New(sk_X509_num(sk)); - for (i = 0; i < sk_X509_num(sk); i++) { - cert = new_x509(sk_X509_value(sk, i), 1); - if (!cert) { - /* XXX Untested */ - Py_DECREF(chain); - return NULL; - } - CRYPTO_add(&cert->x509->references, 1, CRYPTO_LOCK_X509); - PyList_SET_ITEM(chain, i, (PyObject *)cert); - } - return chain; - } else { - Py_INCREF(Py_None); - return Py_None; - } - -} - -static char ssl_Connection_want_read_doc[] = "\n\ -Checks if more data has to be read from the transport layer to complete an\n\ -operation.\n\ -\n\ -@return: True iff more data has to be read\n\ -"; -static PyObject * -ssl_Connection_want_read(ssl_ConnectionObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":want_read")) - return NULL; - - return PyLong_FromLong((long)SSL_want_read(self->ssl)); -} - -static char ssl_Connection_want_write_doc[] = "\n\ -Checks if there is data to write to the transport layer to complete an\n\ -operation.\n\ -\n\ -@return: True iff there is data to write\n\ -"; -static PyObject * -ssl_Connection_want_write(ssl_ConnectionObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":want_write")) - return NULL; - - return PyLong_FromLong((long)SSL_want_write(self->ssl)); -} - -/* - * Member methods in the Connection object - * ADD_METHOD(name) expands to a correct PyMethodDef declaration - * { 'name', (PyCFunction)ssl_Connection_name, METH_VARARGS } - * for convenience - * ADD_ALIAS(name,real) creates an "alias" of the ssl_Connection_real - * function with the name 'name' - */ -#define ADD_METHOD(name) \ - { #name, (PyCFunction)ssl_Connection_##name, METH_VARARGS, ssl_Connection_##name##_doc } -#define ADD_ALIAS(name,real) \ - { #name, (PyCFunction)ssl_Connection_##real, METH_VARARGS, ssl_Connection_##real##_doc } -static PyMethodDef ssl_Connection_methods[] = -{ - ADD_METHOD(get_context), - ADD_METHOD(set_context), - ADD_METHOD(get_servername), - ADD_METHOD(set_tlsext_host_name), - ADD_METHOD(pending), - ADD_METHOD(send), - ADD_ALIAS (write, send), - ADD_METHOD(sendall), - ADD_METHOD(recv), - ADD_ALIAS (read, recv), - ADD_METHOD(bio_read), - ADD_METHOD(bio_write), - ADD_METHOD(renegotiate), - ADD_METHOD(do_handshake), -#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00907000L - ADD_METHOD(renegotiate_pending), -#endif - ADD_METHOD(total_renegotiations), - ADD_METHOD(connect), - ADD_METHOD(connect_ex), - ADD_METHOD(accept), - ADD_METHOD(bio_shutdown), - ADD_METHOD(shutdown), - ADD_METHOD(get_cipher_list), - ADD_METHOD(get_client_ca_list), - ADD_METHOD(makefile), - ADD_METHOD(get_app_data), - ADD_METHOD(set_app_data), - ADD_METHOD(get_shutdown), - ADD_METHOD(set_shutdown), - ADD_METHOD(state_string), - ADD_METHOD(server_random), - ADD_METHOD(client_random), - ADD_METHOD(master_key), - ADD_METHOD(sock_shutdown), - ADD_METHOD(get_peer_certificate), - ADD_METHOD(get_peer_cert_chain), - ADD_METHOD(want_read), - ADD_METHOD(want_write), - ADD_METHOD(set_accept_state), - ADD_METHOD(set_connect_state), - { NULL, NULL } -}; -#undef ADD_ALIAS -#undef ADD_METHOD - -static char ssl_Connection_doc[] = "\n\ -Connection(context, socket) -> Connection instance\n\ -\n\ -Create a new Connection object, using the given OpenSSL.SSL.Context instance\n\ -and socket.\n\ -\n\ -@param context: An SSL Context to use for this connection\n\ -@param socket: The socket to use for transport layer\n\ -"; - -/* - * Initializer used by ssl_Connection_new and ssl_Connection_New. *Not* - * tp_init. This takes an already allocated ssl_ConnectionObj, a context, and - * a optionally a socket, and glues them all together. - */ -static ssl_ConnectionObj* -ssl_Connection_init(ssl_ConnectionObj *self, ssl_ContextObj *ctx, PyObject *sock) { - int fd; - - Py_INCREF(ctx); - self->context = ctx; - - Py_INCREF(sock); - self->socket = sock; - - self->ssl = NULL; - self->from_ssl = NULL; - self->into_ssl = NULL; - - Py_INCREF(Py_None); - self->app_data = Py_None; - - self->tstate = NULL; - - self->ssl = SSL_new(self->context->ctx); - SSL_set_app_data(self->ssl, self); - - if (self->socket == Py_None) - { - /* If it's not a socket or file, treat it like a memory buffer, - * so crazy people can do things like EAP-TLS. */ - self->into_ssl = BIO_new(BIO_s_mem()); - self->from_ssl = BIO_new(BIO_s_mem()); - if (self->into_ssl == NULL || self->from_ssl == NULL) - goto error; - SSL_set_bio(self->ssl, self->into_ssl, self->from_ssl); - } - else - { - fd = PyObject_AsFileDescriptor(self->socket); - if (fd < 0) - { - Py_DECREF(self); - return NULL; - } - else - { - SSL_set_fd(self->ssl, (SOCKET_T)fd); - } - } - return self; - -error: - BIO_free(self->into_ssl); /* NULL safe */ - BIO_free(self->from_ssl); /* NULL safe */ - Py_DECREF(self); - return NULL; -} - -/* - * Constructor for Connection objects - * - * Arguments: ctx - An SSL Context to use for this connection - * sock - The socket to use for transport layer - * Returns: The newly created Connection object - */ -ssl_ConnectionObj * -ssl_Connection_New(ssl_ContextObj *ctx, PyObject *sock) { - ssl_ConnectionObj *self; - - self = PyObject_GC_New(ssl_ConnectionObj, &ssl_Connection_Type); - if (self == NULL) { - return NULL; - } - self = ssl_Connection_init(self, ctx, sock); - if (self == NULL) { - return NULL; - } - PyObject_GC_Track((PyObject *)self); - return self; -} - -static PyObject* -ssl_Connection_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { - ssl_ConnectionObj *self; - ssl_ContextObj *ctx; - PyObject *sock; - static char *kwlist[] = {"context", "socket", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O:Connection", kwlist, - &ssl_Context_Type, &ctx, &sock)) { - return NULL; - } - - self = (ssl_ConnectionObj *)subtype->tp_alloc(subtype, 1); - if (self == NULL) { - return NULL; - } - - return (PyObject *)ssl_Connection_init(self, ctx, sock); -} - -/* - * Find attribute - * - * Arguments: self - The Connection object - * name - The attribute name - * Returns: A Python object for the attribute, or NULL if something went - * wrong - */ -static PyObject * -ssl_Connection_getattro(ssl_ConnectionObj *self, PyObject *nameobj) { - PyObject *meth; - - meth = PyObject_GenericGetAttr((PyObject*)self, nameobj); - if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - /* Try looking it up in the "socket" instead. */ - meth = PyObject_GenericGetAttr(self->socket, nameobj); - } - - return meth; -} - -/* - * Call the visitproc on all contained objects. - * - * Arguments: self - The Connection object - * visit - Function to call - * arg - Extra argument to visit - * Returns: 0 if all goes well, otherwise the return code from the first - * call that gave non-zero result. - */ -static int -ssl_Connection_traverse(ssl_ConnectionObj *self, visitproc visit, void *arg) -{ - int ret = 0; - - if (ret == 0 && self->context != NULL) - ret = visit((PyObject *)self->context, arg); - if (ret == 0 && self->socket != NULL) - ret = visit(self->socket, arg); - if (ret == 0 && self->app_data != NULL) - ret = visit(self->app_data, arg); - return ret; -} - -/* - * Decref all contained objects and zero the pointers. - * - * Arguments: self - The Connection object - * Returns: Always 0. - */ -static int -ssl_Connection_clear(ssl_ConnectionObj *self) -{ - Py_XDECREF(self->context); - self->context = NULL; - Py_XDECREF(self->socket); - self->socket = NULL; - Py_XDECREF(self->app_data); - self->app_data = NULL; - self->into_ssl = NULL; /* was cleaned up by SSL_free() */ - self->from_ssl = NULL; /* was cleaned up by SSL_free() */ - return 0; -} - -/* - * Deallocate the memory used by the Connection object - * - * Arguments: self - The Connection object - * Returns: None - */ -static void -ssl_Connection_dealloc(ssl_ConnectionObj *self) -{ - PyObject_GC_UnTrack(self); - if (self->ssl != NULL) - SSL_free(self->ssl); - ssl_Connection_clear(self); - PyObject_GC_Del(self); -} - -PyTypeObject ssl_Connection_Type = { - PyOpenSSL_HEAD_INIT(&PyType_Type, 0) - "OpenSSL.SSL.Connection", - sizeof(ssl_ConnectionObj), - 0, - (destructor)ssl_Connection_dealloc, - NULL, /* print */ - NULL, /* tp_getattr */ - NULL, /* setattr */ - NULL, /* compare */ - NULL, /* repr */ - NULL, /* as_number */ - NULL, /* as_sequence */ - NULL, /* as_mapping */ - NULL, /* hash */ - NULL, /* call */ - NULL, /* str */ - (getattrofunc)ssl_Connection_getattro, /* getattro */ - NULL, /* setattro */ - NULL, /* as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, - ssl_Connection_doc, /* doc */ - (traverseproc)ssl_Connection_traverse, - (inquiry)ssl_Connection_clear, - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - ssl_Connection_methods, /* tp_methods */ - NULL, /* tp_members */ - NULL, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - NULL, /* tp_init */ - NULL, /* tp_alloc */ - ssl_Connection_new, /* tp_new */ -}; - - -/* - * Initiailze the Connection part of the SSL sub module - * - * Arguments: dict - The OpenSSL.SSL module - * Returns: 1 for success, 0 otherwise - */ -int -init_ssl_connection(PyObject *module) { - - if (PyType_Ready(&ssl_Connection_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&ssl_Connection_Type); - if (PyModule_AddObject(module, "Connection", (PyObject *)&ssl_Connection_Type) != 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&ssl_Connection_Type); - if (PyModule_AddObject(module, "ConnectionType", (PyObject *)&ssl_Connection_Type) != 0) { - return 0; - } - - return 1; -} - diff --git a/OpenSSL/ssl/connection.h b/OpenSSL/ssl/connection.h deleted file mode 100644 index 59f659b..0000000 --- a/OpenSSL/ssl/connection.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * connection.h - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * Export SSL Connection data structures and functions. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - * - */ -#ifndef PyOpenSSL_SSL_CONNECTION_H_ -#define PyOpenSSL_SSL_CONNECTION_H_ - -#include -#include - -/* shamelessly stolen from socketmodule.c */ -#ifdef MS_WINDOWS -# include -typedef SOCKET SOCKET_T; -# ifdef MS_WIN64 -# define SIZEOF_SOCKET_T 8 -# else -# define SIZEOF_SOCKET_T 4 -# endif -#else -typedef int SOCKET_T; -# define SIZEOF_SOCKET_T SIZEOF_INT -#endif - - -extern int init_ssl_connection (PyObject *); - -extern PyTypeObject ssl_Connection_Type; - -#define ssl_Connection_Check(v) ((v)->ob_type == &ssl_Connection_Type) - -typedef struct { - PyObject_HEAD - SSL *ssl; - ssl_ContextObj *context; - PyObject *socket; - PyThreadState *tstate; /* This field is no longer used. */ - PyObject *app_data; - BIO *into_ssl, *from_ssl; /* for connections without file descriptors */ -} ssl_ConnectionObj; - - - -#endif - diff --git a/OpenSSL/ssl/context.c b/OpenSSL/ssl/context.c deleted file mode 100644 index c2bdcab..0000000 --- a/OpenSSL/ssl/context.c +++ /dev/null @@ -1,1419 +0,0 @@ -/* - * context.c - * - * Copyright (C) AB Strakt - * Copyright (C) Jean-Paul Calderone - * See LICENSE for details. - * - * SSL Context objects and their methods. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - */ -#include - -#if PY_VERSION_HEX >= 0x02050000 -# define PYARG_PARSETUPLE_FORMAT const char -# define PYOBJECT_GETATTRSTRING_TYPE const char* -#else -# define PYARG_PARSETUPLE_FORMAT char -# define PYOBJECT_GETATTRSTRING_TYPE char* -#endif - -#ifndef MS_WINDOWS -# include -# include -# if !(defined(__BEOS__) || defined(__CYGWIN__)) -# include -# endif -#else -# include -# include -#endif - -#define SSL_MODULE -#include "ssl.h" - -/* - * CALLBACKS - * - * Callbacks work like this: We provide a "global" callback in C which - * transforms the arguments into a Python argument tuple and calls the - * corresponding Python callback, and then parsing the return value back into - * things the C function can return. - * - * Three caveats: - * + How do we find the Context object where the Python callbacks are stored? - * + What about multithreading and execution frames? - * + What about Python callbacks that raise exceptions? - * - * The solution to the first issue is trivial if the callback provides - * "userdata" functionality. Since the only callbacks that don't provide - * userdata do provide a pointer to an SSL structure, we can associate an SSL - * object and a Connection one-to-one via the SSL_set/get_app_data() - * functions. - * - * The solution to the other issue is to rewrite the Py_BEGIN_ALLOW_THREADS - * macro allowing it (or rather a new macro) to specify where to save the - * thread state (in our case, as a member of the Connection/Context object) so - * we can retrieve it again before calling the Python 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 - * verify - If true, the passphrase callback should ask for a - * password twice and verify they're equal. If false, only - * ask once. - * arg - User data, always a Context object - * Returns: The length of the password if successful, 0 otherwise - */ -static int -global_passphrase_callback(char *buf, int maxlen, int verify, void *arg) -{ - /* - * 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); - - /* - * 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) { - /* - * The callback raised an exception. It will be raised by whatever - * Python API triggered this callback. - */ - goto out; - } - - if (!PyObject_IsTrue(ret)) { - /* - * Returned "", or None, or something. Treat it as no passphrase. - */ - Py_DECREF(ret); - goto out; - } - - if (!PyBytes_Check(ret)) { - /* - * XXX Returned something that wasn't a string. This is bogus. We'll - * return 0 and OpenSSL will treat it as an error, resulting in an - * exception from whatever Python API triggered this callback. - */ - Py_DECREF(ret); - goto out; - } - - len = PyBytes_Size(ret); - if (len > maxlen) { - /* - * Returned more than we said they were allowed to return. Just - * truncate it. Might be better to raise an exception, - * instead. -exarkun - */ - len = maxlen; - } - - str = PyBytes_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; -} - -/* - * Globally defined verify callback - * - * Arguments: ok - True everything is OK "so far", false otherwise - * x509_ctx - Contains the certificate being checked, the current - * error number and depth, and the Connection we're - * dealing with - * Returns: True if everything is okay, false otherwise - */ -static int -global_verify_callback(int ok, X509_STORE_CTX *x509_ctx) -{ - PyObject *argv, *ret; - SSL *ssl; - ssl_ConnectionObj *conn; - crypto_X509Obj *cert; - int errnum, errdepth, c_ret; - - // 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); - - MY_END_ALLOW_THREADS(conn->tstate); - - cert = new_x509(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); - ret = PyEval_CallObject(conn->context->verify_callback, argv); - Py_DECREF(argv); - - if (ret != NULL && PyObject_IsTrue(ret)) { - X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); - Py_DECREF(ret); - c_ret = 1; - } else { - c_ret = 0; - } - - MY_BEGIN_ALLOW_THREADS(conn->tstate); - return c_ret; -} - -/* - * 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 - * _ret - The return code of the SSL function that called us - * Returns: None - */ -static void -global_info_callback(const SSL *ssl, int where, int _ret) -{ - 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); - 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; -} - -/* - * Globally defined TLS extension server name 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. - * - * ssl represents the connection this callback is for - * - * alert is a pointer to the alert value which maybe will be emitted to the - * client if there is an error handling the client hello (which contains the - * server name). This is an out parameter, maybe. - * - * arg is an arbitrary pointer specified by SSL_CTX_set_tlsext_servername_arg. - * It will be NULL for all pyOpenSSL uses. - */ -static int -global_tlsext_servername_callback(const SSL *ssl, int *alert, void *arg) { - int result = 0; - PyObject *argv, *ret; - ssl_ConnectionObj *conn = (ssl_ConnectionObj *)SSL_get_app_data(ssl); - - /* - * 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("(O)", (PyObject *)conn); - ret = PyEval_CallObject(conn->context->tlsext_servername_callback, argv); - Py_DECREF(argv); - Py_DECREF(ret); - - /* - * This function is returning into OpenSSL. Release the GIL again. - */ - MY_BEGIN_ALLOW_THREADS(conn->tstate); - return result; -} - -/* - * More recent builds of OpenSSL may have SSLv2 completely disabled. - */ -#ifdef OPENSSL_NO_SSL2 -#define SSLv2_METHOD_TEXT "" -#else -#define SSLv2_METHOD_TEXT "SSLv2_METHOD, " -#endif - - -static char ssl_Context_doc[] = "\n\ -Context(method) -> Context instance\n\ -\n\ -OpenSSL.SSL.Context instances define the parameters for setting up new SSL\n\ -connections.\n\ -\n\ -@param method: One of " SSLv2_METHOD_TEXT "SSLv3_METHOD, SSLv23_METHOD, or\n\ - TLSv1_METHOD.\n\ -"; - -#undef SSLv2_METHOD_TEXT - -static char ssl_Context_load_verify_locations_doc[] = "\n\ -Let SSL know where we can find trusted certificates for the certificate\n\ -chain\n\ -\n\ -@param cafile: In which file we can find the certificates\n\ -@param capath: In which directory we can find the certificates\n\ -@return: None\n\ -"; -static PyObject * -ssl_Context_load_verify_locations(ssl_ContextObj *self, PyObject *args) { - char *cafile = NULL; - char *capath = NULL; - - if (!PyArg_ParseTuple(args, "z|z:load_verify_locations", &cafile, &capath)) { - return NULL; - } - - if (!SSL_CTX_load_verify_locations(self->ctx, cafile, capath)) - { - exception_from_error_queue(ssl_Error); - return NULL; - } - else - { - Py_INCREF(Py_None); - return Py_None; - } -} - -static char ssl_Context_set_default_verify_paths_doc[] = "\n\ -Use the platform-specific CA certificate locations\n\ -\n\ -@return: 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(ssl_Error); - return NULL; - } - Py_INCREF(Py_None); - return Py_None; -}; - - -static char ssl_Context_set_passwd_cb_doc[] = "\n\ -Set the passphrase callback\n\ -\n\ -@param callback: The Python callback to use\n\ -@param userdata: (optional) A Python object which will be given as\n\ - argument to the callback\n\ -@return: None\n\ -"; -static PyObject * -ssl_Context_set_passwd_cb(ssl_ContextObj *self, PyObject *args) -{ - PyObject *callback = NULL, *userdata = Py_None; - - if (!PyArg_ParseTuple(args, "O|O:set_passwd_cb", &callback, &userdata)) - return NULL; - - if (!PyCallable_Check(callback)) - { - PyErr_SetString(PyExc_TypeError, "expected PyCallable"); - return NULL; - } - - Py_DECREF(self->passphrase_callback); - Py_INCREF(callback); - self->passphrase_callback = callback; - SSL_CTX_set_default_passwd_cb(self->ctx, global_passphrase_callback); - - Py_DECREF(self->passphrase_userdata); - Py_INCREF(userdata); - self->passphrase_userdata = userdata; - SSL_CTX_set_default_passwd_cb_userdata(self->ctx, (void *)self); - - Py_INCREF(Py_None); - return Py_None; -} - -static PyTypeObject * -type_modified_error(const char *name) { - PyErr_Format(PyExc_RuntimeError, - "OpenSSL.crypto's '%s' attribute has been modified", - name); - return NULL; -} - -static PyTypeObject * -import_crypto_type(const char *name, size_t objsize) { - PyObject *module, *type, *name_attr; - PyTypeObject *res; - int right_name; - - module = PyImport_ImportModule("OpenSSL.crypto"); - if (module == NULL) { - return NULL; - } - type = PyObject_GetAttrString(module, (PYOBJECT_GETATTRSTRING_TYPE)name); - Py_DECREF(module); - if (type == NULL) { - return NULL; - } - if (!(PyType_Check(type))) { - Py_DECREF(type); - return type_modified_error(name); - } - name_attr = PyObject_GetAttrString(type, "__name__"); - if (name_attr == NULL) { - Py_DECREF(type); - return NULL; - } - -#ifdef PY3 - { - PyObject* asciiname = PyUnicode_AsASCIIString(name_attr); - Py_DECREF(name_attr); - name_attr = asciiname; - } -#endif - right_name = (PyBytes_CheckExact(name_attr) && - strcmp(name, PyBytes_AsString(name_attr)) == 0); - Py_DECREF(name_attr); - res = (PyTypeObject *)type; - if (!right_name || res->tp_basicsize != objsize) { - Py_DECREF(type); - return type_modified_error(name); - } - return res; -} - -static crypto_X509Obj * -parse_certificate_argument(const char* format, PyObject* args) { - static PyTypeObject *crypto_X509_type = NULL; - crypto_X509Obj *cert; - - if (!crypto_X509_type) { - crypto_X509_type = import_crypto_type("X509", sizeof(crypto_X509Obj)); - if (!crypto_X509_type) { - return NULL; - } - } - if (!PyArg_ParseTuple(args, (PYARG_PARSETUPLE_FORMAT *)format, - crypto_X509_type, &cert)) { - return NULL; - } - return cert; -} - -static char ssl_Context_add_extra_chain_cert_doc[] = "\n\ -Add certificate to chain\n\ -\n\ -@param certobj: The X509 certificate object to add to the chain\n\ -@return: None\n\ -"; - -static PyObject * -ssl_Context_add_extra_chain_cert(ssl_ContextObj *self, PyObject *args) -{ - X509* cert_original; - crypto_X509Obj *cert = parse_certificate_argument( - "O!:add_extra_chain_cert", args); - if (cert == NULL) - { - return NULL; - } - if (!(cert_original = X509_dup(cert->x509))) - { - /* exception_from_error_queue(ssl_Error); */ - PyErr_SetString(PyExc_RuntimeError, "X509_dup failed"); - return NULL; - } - if (!SSL_CTX_add_extra_chain_cert(self->ctx, cert_original)) - { - X509_free(cert_original); - exception_from_error_queue(ssl_Error); - return NULL; - } - else - { - Py_INCREF(Py_None); - return Py_None; - } -} - - -static char ssl_Context_use_certificate_chain_file_doc[] = "\n\ -Load a certificate chain from a file\n\ -\n\ -@param certfile: The name of the certificate chain file\n\ -@return: None\n\ -"; -static PyObject * -ssl_Context_use_certificate_chain_file(ssl_ContextObj *self, PyObject *args) -{ - char *certfile; - - if (!PyArg_ParseTuple(args, "s:use_certificate_chain_file", &certfile)) - return NULL; - - if (!SSL_CTX_use_certificate_chain_file(self->ctx, certfile)) - { - exception_from_error_queue(ssl_Error); - return NULL; - } - else - { - Py_INCREF(Py_None); - return Py_None; - } -} - - -static char ssl_Context_use_certificate_file_doc[] = "\n\ -Load a certificate from a file\n\ -\n\ -@param certfile: The name of the certificate file\n\ -@param filetype: (optional) The encoding of the file, default is PEM\n\ -@return: None\n\ -"; -static PyObject * -ssl_Context_use_certificate_file(ssl_ContextObj *self, PyObject *args) -{ - char *certfile; - int filetype = SSL_FILETYPE_PEM; - - if (!PyArg_ParseTuple(args, "s|i:use_certificate_file", &certfile, &filetype)) - return NULL; - - if (!SSL_CTX_use_certificate_file(self->ctx, certfile, filetype)) - { - exception_from_error_queue(ssl_Error); - return NULL; - } - else - { - Py_INCREF(Py_None); - return Py_None; - } -} - -static char ssl_Context_use_certificate_doc[] = "\n\ -Load a certificate from a X509 object\n\ -\n\ -@param cert: The X509 object\n\ -@return: None\n\ -"; -static PyObject * -ssl_Context_use_certificate(ssl_ContextObj *self, PyObject *args) -{ - crypto_X509Obj *cert = parse_certificate_argument( - "O!:use_certificate", args); - if (cert == NULL) { - return NULL; - } - - if (!SSL_CTX_use_certificate(self->ctx, cert->x509)) - { - exception_from_error_queue(ssl_Error); - return NULL; - } - else - { - Py_INCREF(Py_None); - return Py_None; - } -} - -static char ssl_Context_use_privatekey_file_doc[] = "\n\ -Load a private key from a file\n\ -\n\ -@param keyfile: The name of the key file\n\ -@param filetype: (optional) The encoding of the file, default is PEM\n\ -@return: None\n\ -"; -static PyObject * -ssl_Context_use_privatekey_file(ssl_ContextObj *self, PyObject *args) -{ - char *keyfile; - int filetype = SSL_FILETYPE_PEM, ret; - - if (!PyArg_ParseTuple(args, "s|i:use_privatekey_file", &keyfile, &filetype)) - return NULL; - - MY_BEGIN_ALLOW_THREADS(self->tstate); - ret = SSL_CTX_use_PrivateKey_file(self->ctx, keyfile, filetype); - MY_END_ALLOW_THREADS(self->tstate); - - if (PyErr_Occurred()) - { - flush_error_queue(); - return NULL; - } - - if (!ret) - { - exception_from_error_queue(ssl_Error); - return NULL; - } - else - { - Py_INCREF(Py_None); - return Py_None; - } -} - -static char ssl_Context_use_privatekey_doc[] = "\n\ -Load a private key from a PKey object\n\ -\n\ -@param pkey: The PKey object\n\ -@return: None\n\ -"; -static PyObject * -ssl_Context_use_privatekey(ssl_ContextObj *self, PyObject *args) { - static PyTypeObject *crypto_PKey_type = NULL; - crypto_PKeyObj *pkey; - - if (!crypto_PKey_type) { - crypto_PKey_type = import_crypto_type("PKey", sizeof(crypto_PKeyObj)); - if (!crypto_PKey_type) { - return NULL; - } - } - if (!PyArg_ParseTuple(args, "O!:use_privatekey", crypto_PKey_type, &pkey)) { - return NULL; - } - - if (!SSL_CTX_use_PrivateKey(self->ctx, pkey->pkey)) { - exception_from_error_queue(ssl_Error); - return NULL; - } else { - Py_INCREF(Py_None); - return Py_None; - } -} - -static char ssl_Context_check_privatekey_doc[] = "\n\ -Check that the private key and certificate match up\n\ -\n\ -@return: None (raises an exception if something's wrong)\n\ -"; -static PyObject * -ssl_Context_check_privatekey(ssl_ContextObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":check_privatekey")) - return NULL; - - if (!SSL_CTX_check_private_key(self->ctx)) - { - exception_from_error_queue(ssl_Error); - return NULL; - } - else - { - Py_INCREF(Py_None); - return Py_None; - } -} - -static char ssl_Context_load_client_ca_doc[] = "\n\ -Load the trusted certificates that will be sent to the client (basically\n \ -telling the client \"These are the guys I trust\"). Does not actually\n\ -imply any of the certificates are trusted; that must be configured\n\ -separately.\n\ -\n\ -@param cafile: The name of the certificates file\n\ -@return: None\n\ -"; -static PyObject * -ssl_Context_load_client_ca(ssl_ContextObj *self, PyObject *args) -{ - char *cafile; - - if (!PyArg_ParseTuple(args, "s:load_client_ca", &cafile)) - return NULL; - - SSL_CTX_set_client_CA_list(self->ctx, SSL_load_client_CA_file(cafile)); - - Py_INCREF(Py_None); - return Py_None; -} - -static char ssl_Context_set_session_id_doc[] = "\n\ -Set the session identifier, this is needed if you want to do session\n\ -resumption (which, ironically, isn't implemented yet)\n\ -\n\ -@param buf: A Python object that can be safely converted to a string\n\ -@returns: None\n\ -"; -static PyObject * -ssl_Context_set_session_id(ssl_ContextObj *self, PyObject *args) -{ - unsigned char *buf; - unsigned int len; - - if (!PyArg_ParseTuple(args, "s#:set_session_id", &buf, &len)) - return NULL; - - if (!SSL_CTX_set_session_id_context(self->ctx, buf, len)) - { - exception_from_error_queue(ssl_Error); - return NULL; - } - else - { - Py_INCREF(Py_None); - return Py_None; - } -} - -static char ssl_Context_set_verify_doc[] = "\n\ -Set the verify mode and verify callback\n\ -\n\ -@param mode: The verify mode, this is either VERIFY_NONE or\n\ - VERIFY_PEER combined with possible other flags\n\ -@param callback: The Python callback to use\n\ -@return: None\n\ -\n\ -See SSL_CTX_set_verify(3SSL) for further details.\n\ -"; -static PyObject * -ssl_Context_set_verify(ssl_ContextObj *self, PyObject *args) -{ - int mode; - PyObject *callback = NULL; - - if (!PyArg_ParseTuple(args, "iO:set_verify", &mode, &callback)) - return NULL; - - if (!PyCallable_Check(callback)) - { - PyErr_SetString(PyExc_TypeError, "expected PyCallable"); - return NULL; - } - - Py_DECREF(self->verify_callback); - Py_INCREF(callback); - self->verify_callback = callback; - SSL_CTX_set_verify(self->ctx, mode, global_verify_callback); - - Py_INCREF(Py_None); - return Py_None; -} - -static char ssl_Context_set_verify_depth_doc[] = "\n\ -Set the verify depth\n\ -\n\ -@param depth: An integer specifying the verify depth\n\ -@return: None\n\ -"; -static PyObject * -ssl_Context_set_verify_depth(ssl_ContextObj *self, PyObject *args) -{ - int depth; - - if (!PyArg_ParseTuple(args, "i:set_verify_depth", &depth)) - return NULL; - - SSL_CTX_set_verify_depth(self->ctx, depth); - Py_INCREF(Py_None); - return Py_None; -} - -static char ssl_Context_get_verify_mode_doc[] = "\n\ -Get the verify mode\n\ -\n\ -@return: The verify mode\n\ -"; -static PyObject * -ssl_Context_get_verify_mode(ssl_ContextObj *self, PyObject *args) -{ - int mode; - - if (!PyArg_ParseTuple(args, ":get_verify_mode")) - return NULL; - - mode = SSL_CTX_get_verify_mode(self->ctx); - return PyLong_FromLong((long)mode); -} - -static char ssl_Context_get_verify_depth_doc[] = "\n\ -Get the verify depth\n\ -\n\ -@return: The verify depth\n\ -"; -static PyObject * -ssl_Context_get_verify_depth(ssl_ContextObj *self, PyObject *args) -{ - int depth; - - if (!PyArg_ParseTuple(args, ":get_verify_depth")) - return NULL; - - depth = SSL_CTX_get_verify_depth(self->ctx); - return PyLong_FromLong((long)depth); -} - -static char ssl_Context_load_tmp_dh_doc[] = "\n\ -Load parameters for Ephemeral Diffie-Hellman\n\ -\n\ -@param dhfile: The file to load EDH parameters from\n\ -@return: None\n\ -"; -static PyObject * -ssl_Context_load_tmp_dh(ssl_ContextObj *self, PyObject *args) -{ - char *dhfile; - BIO *bio; - DH *dh; - - if (!PyArg_ParseTuple(args, "s:load_tmp_dh", &dhfile)) - return NULL; - - bio = BIO_new_file(dhfile, "r"); - if (bio == NULL) { - exception_from_error_queue(ssl_Error); - return NULL; - } - - dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); - SSL_CTX_set_tmp_dh(self->ctx, dh); - DH_free(dh); - BIO_free(bio); - - Py_INCREF(Py_None); - return Py_None; -} - -static char ssl_Context_set_cipher_list_doc[] = "\n\ -Change the cipher list\n\ -\n\ -@param cipher_list: A cipher list, see ciphers(1)\n\ -@return: None\n\ -"; -static PyObject * -ssl_Context_set_cipher_list(ssl_ContextObj *self, PyObject *args) -{ - char *cipher_list; - - if (!PyArg_ParseTuple(args, "s:set_cipher_list", &cipher_list)) - return NULL; - - if (!SSL_CTX_set_cipher_list(self->ctx, cipher_list)) - { - exception_from_error_queue(ssl_Error); - return NULL; - } - else - { - Py_INCREF(Py_None); - return Py_None; - } -} - -static char ssl_Context_set_client_ca_list_doc[] = "\n\ -Set the list of preferred client certificate signers for this server context.\n\ -\n\ -This list of certificate authorities will be sent to the client when the\n\ -server requests a client certificate.\n\ -\n\ -@param certificate_authorities: a sequence of X509Names.\n\ -@return: None\n\ -"; - -static PyObject * -ssl_Context_set_client_ca_list(ssl_ContextObj *self, PyObject *args) -{ - static PyTypeObject *X509NameType; - PyObject *sequence, *tuple, *item; - crypto_X509NameObj *name; - X509_NAME *sslname; - STACK_OF(X509_NAME) *CANames; - Py_ssize_t length; - int i; - - if (X509NameType == NULL) { - X509NameType = import_crypto_type("X509Name", sizeof(crypto_X509NameObj)); - if (X509NameType == NULL) { - return NULL; - } - } - if (!PyArg_ParseTuple(args, "O:set_client_ca_list", &sequence)) { - return NULL; - } - tuple = PySequence_Tuple(sequence); - if (tuple == NULL) { - return NULL; - } - length = PyTuple_Size(tuple); - if (length >= INT_MAX) { - PyErr_SetString(PyExc_ValueError, "client CA list is too long"); - Py_DECREF(tuple); - return NULL; - } - CANames = sk_X509_NAME_new_null(); - if (CANames == NULL) { - Py_DECREF(tuple); - exception_from_error_queue(ssl_Error); - return NULL; - } - for (i = 0; i < length; i++) { - item = PyTuple_GetItem(tuple, i); - if (item->ob_type != X509NameType) { - PyErr_Format(PyExc_TypeError, - "client CAs must be X509Name objects, not %s objects", - item->ob_type->tp_name); - sk_X509_NAME_free(CANames); - Py_DECREF(tuple); - return NULL; - } - name = (crypto_X509NameObj *)item; - sslname = X509_NAME_dup(name->x509_name); - if (sslname == NULL) { - sk_X509_NAME_free(CANames); - Py_DECREF(tuple); - exception_from_error_queue(ssl_Error); - return NULL; - } - if (!sk_X509_NAME_push(CANames, sslname)) { - X509_NAME_free(sslname); - sk_X509_NAME_free(CANames); - Py_DECREF(tuple); - exception_from_error_queue(ssl_Error); - return NULL; - } - } - Py_DECREF(tuple); - SSL_CTX_set_client_CA_list(self->ctx, CANames); - Py_INCREF(Py_None); - return Py_None; -} - -static char ssl_Context_add_client_ca_doc[] = "\n\ -Add the CA certificate to the list of preferred signers for this context.\n\ -\n\ -The list of certificate authorities will be sent to the client when the\n\ -server requests a client certificate.\n\ -\n\ -@param certificate_authority: certificate authority's X509 certificate.\n\ -@return: None\n\ -"; - -static PyObject * -ssl_Context_add_client_ca(ssl_ContextObj *self, PyObject *args) -{ - crypto_X509Obj *cert; - - cert = parse_certificate_argument("O!:add_client_ca", args); - if (cert == NULL) { - return NULL; - } - if (!SSL_CTX_add_client_CA(self->ctx, cert->x509)) { - exception_from_error_queue(ssl_Error); - return NULL; - } - Py_INCREF(Py_None); - return Py_None; -} - -static char ssl_Context_set_timeout_doc[] = "\n\ -Set session timeout\n\ -\n\ -@param timeout: The timeout in seconds\n\ -@return: The previous session timeout\n\ -"; -static PyObject * -ssl_Context_set_timeout(ssl_ContextObj *self, PyObject *args) -{ - long t, ret; - - if (!PyArg_ParseTuple(args, "l:set_timeout", &t)) - return NULL; - - ret = SSL_CTX_set_timeout(self->ctx, t); - return PyLong_FromLong(ret); -} - -static char ssl_Context_get_timeout_doc[] = "\n\ -Get the session timeout\n\ -\n\ -@return: The session timeout\n\ -"; -static PyObject * -ssl_Context_get_timeout(ssl_ContextObj *self, PyObject *args) -{ - long ret; - - if (!PyArg_ParseTuple(args, ":get_timeout")) - return NULL; - - ret = SSL_CTX_get_timeout(self->ctx); - return PyLong_FromLong(ret); -} - -static char ssl_Context_set_info_callback_doc[] = "\n\ -Set the info callback\n\ -\n\ -@param callback: The Python callback to use\n\ -@return: None\n\ -"; -static PyObject * -ssl_Context_set_info_callback(ssl_ContextObj *self, PyObject *args) -{ - PyObject *callback; - - if (!PyArg_ParseTuple(args, "O:set_info_callback", &callback)) - return NULL; - - if (!PyCallable_Check(callback)) - { - PyErr_SetString(PyExc_TypeError, "expected PyCallable"); - return NULL; - } - - Py_DECREF(self->info_callback); - Py_INCREF(callback); - self->info_callback = callback; - SSL_CTX_set_info_callback(self->ctx, global_info_callback); - - Py_INCREF(Py_None); - return Py_None; -} - -static char ssl_Context_get_app_data_doc[] = "\n\ -Get the application data (supplied via set_app_data())\n\ -\n\ -@return: The application data\n\ -"; -static PyObject * -ssl_Context_get_app_data(ssl_ContextObj *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":get_app_data")) - return NULL; - - Py_INCREF(self->app_data); - return self->app_data; -} - -static char ssl_Context_set_app_data_doc[] = "\n\ -Set the application data (will be returned from get_app_data())\n\ -\n\ -@param data: Any Python object\n\ -@return: None\n\ -"; -static PyObject * -ssl_Context_set_app_data(ssl_ContextObj *self, PyObject *args) -{ - PyObject *data; - - if (!PyArg_ParseTuple(args, "O:set_app_data", &data)) - return NULL; - - Py_DECREF(self->app_data); - Py_INCREF(data); - self->app_data = data; - - Py_INCREF(Py_None); - return Py_None; -} - -static char ssl_Context_get_cert_store_doc[] = "\n\ -Get the certificate store for the context\n\ -\n\ -@return: A X509Store object\n\ -"; -static PyObject * -ssl_Context_get_cert_store(ssl_ContextObj *self, PyObject *args) -{ - X509_STORE *store; - - if (!PyArg_ParseTuple(args, ":get_cert_store")) - return NULL; - - if ((store = SSL_CTX_get_cert_store(self->ctx)) == NULL) - { - Py_INCREF(Py_None); - return Py_None; - } - else - { - return (PyObject *)new_x509store(store, 0); - } -} - -static char ssl_Context_set_options_doc[] = "\n\ -Add options. Options set before are not cleared!\n\ -\n\ -@param options: The options to add.\n\ -@return: The new option bitmask.\n\ -"; -static PyObject * -ssl_Context_set_options(ssl_ContextObj *self, PyObject *args) -{ - long options; - - if (!PyArg_ParseTuple(args, "l:set_options", &options)) - return NULL; - - return PyLong_FromLong(SSL_CTX_set_options(self->ctx, options)); -} - -static char ssl_Context_set_tlsext_servername_callback_doc[] = "\n\ -Specify a callback function to be called when clients specify a server name.\n\ -\n\ -@param callback: The callback function. It will be invoked with one\n\ - argument, the Connection instance.\n\ -\n\ -"; -static PyObject * -ssl_Context_set_tlsext_servername_callback(ssl_ContextObj *self, PyObject *args) { - PyObject *callback; - PyObject *old; - - if (!PyArg_ParseTuple(args, "O:set_tlsext_servername_callback", &callback)) { - return NULL; - } - - Py_INCREF(callback); - old = self->tlsext_servername_callback; - self->tlsext_servername_callback = callback; - Py_DECREF(old); - - SSL_CTX_set_tlsext_servername_callback(self->ctx, global_tlsext_servername_callback); - SSL_CTX_set_tlsext_servername_arg(self->ctx, NULL); - - Py_INCREF(Py_None); - return Py_None; -} - - -/* - * Member methods in the Context object - * ADD_METHOD(name) expands to a correct PyMethodDef declaration - * { 'name', (PyCFunction)ssl_Context_name, METH_VARARGS } - * for convenience - * ADD_ALIAS(name,real) creates an "alias" of the ssl_Context_real - * function with the name 'name' - */ -#define ADD_METHOD(name) { #name, (PyCFunction)ssl_Context_##name, METH_VARARGS, ssl_Context_##name##_doc } -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), - ADD_METHOD(add_extra_chain_cert), - ADD_METHOD(use_privatekey_file), - ADD_METHOD(use_privatekey), - ADD_METHOD(check_privatekey), - ADD_METHOD(load_client_ca), - ADD_METHOD(set_session_id), - ADD_METHOD(set_verify), - ADD_METHOD(set_verify_depth), - ADD_METHOD(get_verify_mode), - ADD_METHOD(get_verify_depth), - ADD_METHOD(load_tmp_dh), - ADD_METHOD(set_cipher_list), - ADD_METHOD(set_client_ca_list), - ADD_METHOD(add_client_ca), - ADD_METHOD(set_timeout), - ADD_METHOD(get_timeout), - ADD_METHOD(set_info_callback), - ADD_METHOD(get_app_data), - ADD_METHOD(set_app_data), - ADD_METHOD(get_cert_store), - ADD_METHOD(set_options), - ADD_METHOD(set_tlsext_servername_callback), - { NULL, NULL } -}; -#undef ADD_METHOD - -/* - * Despite the name which might suggest otherwise, this is not the tp_init for - * the Context type. It's just the common initialization code shared by the - * two _{Nn}ew functions below. - */ -static ssl_ContextObj* -ssl_Context_init(ssl_ContextObj *self, int i_method) { -#if (OPENSSL_VERSION_NUMBER >> 28) == 0x01 - const -#endif - SSL_METHOD *method; - - switch (i_method) { - case ssl_SSLv2_METHOD: -#ifdef OPENSSL_NO_SSL2 - PyErr_SetString(PyExc_ValueError, "SSLv2_METHOD not supported by this version of OpenSSL"); - return NULL; -#else - method = SSLv2_method(); -#endif - break; - case ssl_SSLv23_METHOD: - method = SSLv23_method(); - break; - case ssl_SSLv3_METHOD: - method = SSLv3_method(); - break; - case ssl_TLSv1_METHOD: - method = TLSv1_method(); - break; - default: - PyErr_SetString(PyExc_ValueError, "No such protocol"); - return NULL; - } - - self->ctx = SSL_CTX_new(method); - Py_INCREF(Py_None); - self->passphrase_callback = Py_None; - Py_INCREF(Py_None); - self->verify_callback = Py_None; - Py_INCREF(Py_None); - self->info_callback = Py_None; - - Py_INCREF(Py_None); - self->tlsext_servername_callback = Py_None; - - Py_INCREF(Py_None); - self->passphrase_userdata = Py_None; - - Py_INCREF(Py_None); - self->app_data = Py_None; - - /* Some initialization that's required to operate smoothly in Python */ - SSL_CTX_set_app_data(self->ctx, self); - SSL_CTX_set_mode(self->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | - SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | - SSL_MODE_AUTO_RETRY); - - self->tstate = NULL; - - return self; -} - -/* - * This one is exposed in the CObject API. I want to deprecate it. - */ -ssl_ContextObj* -ssl_Context_New(int i_method) { - ssl_ContextObj *self; - - self = PyObject_GC_New(ssl_ContextObj, &ssl_Context_Type); - if (self == NULL) { - return (ssl_ContextObj *)PyErr_NoMemory(); - } - self = ssl_Context_init(self, i_method); - PyObject_GC_Track((PyObject *)self); - return self; -} - - -/* - * This one is the tp_new of the Context type. It's great. - */ -static PyObject* -ssl_Context_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { - int i_method; - ssl_ContextObj *self; - static char *kwlist[] = {"method", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i:Context", kwlist, &i_method)) { - return NULL; - } - - self = (ssl_ContextObj *)subtype->tp_alloc(subtype, 1); - if (self == NULL) { - return NULL; - } - - return (PyObject *)ssl_Context_init(self, i_method); -} - -/* - * Call the visitproc on all contained objects. - * - * Arguments: self - The Context object - * visit - Function to call - * arg - Extra argument to visit - * Returns: 0 if all goes well, otherwise the return code from the first - * call that gave non-zero result. - */ -static int -ssl_Context_traverse(ssl_ContextObj *self, visitproc visit, void *arg) -{ - int ret = 0; - - if (ret == 0 && self->passphrase_callback != NULL) - ret = visit((PyObject *)self->passphrase_callback, arg); - if (ret == 0 && self->passphrase_userdata != NULL) - ret = visit((PyObject *)self->passphrase_userdata, arg); - if (ret == 0 && self->verify_callback != NULL) - ret = visit((PyObject *)self->verify_callback, arg); - if (ret == 0 && self->info_callback != NULL) - ret = visit((PyObject *)self->info_callback, arg); - if (ret == 0 && self->app_data != NULL) - ret = visit(self->app_data, arg); - return ret; -} - -/* - * Decref all contained objects and zero the pointers. - * - * Arguments: self - The Context object - * Returns: Always 0. - */ -static int -ssl_Context_clear(ssl_ContextObj *self) -{ - Py_XDECREF(self->passphrase_callback); - self->passphrase_callback = NULL; - Py_XDECREF(self->passphrase_userdata); - self->passphrase_userdata = NULL; - Py_XDECREF(self->verify_callback); - self->verify_callback = NULL; - Py_XDECREF(self->info_callback); - self->info_callback = NULL; - Py_XDECREF(self->app_data); - self->app_data = NULL; - return 0; -} - -/* - * Deallocate the memory used by the Context object - * - * Arguments: self - The Context object - * Returns: None - */ -static void -ssl_Context_dealloc(ssl_ContextObj *self) -{ - PyObject_GC_UnTrack((PyObject *)self); - SSL_CTX_free(self->ctx); - ssl_Context_clear(self); - PyObject_GC_Del(self); -} - - -PyTypeObject ssl_Context_Type = { - PyOpenSSL_HEAD_INIT(&PyType_Type, 0) - "OpenSSL.SSL.Context", - sizeof(ssl_ContextObj), - 0, - (destructor)ssl_Context_dealloc, /* tp_dealloc */ - NULL, /* print */ - NULL, /* tp_getattr */ - NULL, /* setattr */ - NULL, /* compare */ - NULL, /* repr */ - NULL, /* as_number */ - NULL, /* as_sequence */ - NULL, /* as_mapping */ - NULL, /* hash */ - NULL, /* call */ - NULL, /* str */ - NULL, /* getattro */ - NULL, /* setattro */ - NULL, /* as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ - ssl_Context_doc, /* tp_doc */ - (traverseproc)ssl_Context_traverse, /* tp_traverse */ - (inquiry)ssl_Context_clear, /* tp_clear */ - NULL, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - NULL, /* tp_iter */ - NULL, /* tp_iternext */ - ssl_Context_methods, /* tp_methods */ - NULL, /* tp_members */ - NULL, /* tp_getset */ - NULL, /* tp_base */ - NULL, /* tp_dict */ - NULL, /* tp_descr_get */ - NULL, /* tp_descr_set */ - 0, /* tp_dictoffset */ - NULL, /* tp_init */ - NULL, /* tp_alloc */ - ssl_Context_new, /* tp_new */ -}; - - -/* - * Initialize the Context part of the SSL sub module - * - * Arguments: dict - The OpenSSL.SSL module - * Returns: 1 for success, 0 otherwise - */ -int -init_ssl_context(PyObject *module) { - - if (PyType_Ready(&ssl_Context_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&ssl_Context_Type); - if (PyModule_AddObject(module, "Context", (PyObject *)&ssl_Context_Type) < 0) { - return 0; - } - - /* PyModule_AddObject steals a reference. - */ - Py_INCREF((PyObject *)&ssl_Context_Type); - if (PyModule_AddObject(module, "ContextType", (PyObject *)&ssl_Context_Type) < 0) { - return 0; - } - - return 1; -} - diff --git a/OpenSSL/ssl/context.h b/OpenSSL/ssl/context.h deleted file mode 100644 index 19b5e9e..0000000 --- a/OpenSSL/ssl/context.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * context.h - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * Export SSL Context object data structures and functions. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - * - */ -#ifndef PyOpenSSL_SSL_CONTEXT_H_ -#define PyOpenSSL_SSL_CONTEXT_H_ - -#include -#include - -extern int init_ssl_context (PyObject *); - -extern PyTypeObject ssl_Context_Type; - -#define ssl_Context_Check(v) ((v)->ob_type == &ssl_Context_Type) - -typedef struct { - PyObject_HEAD - SSL_CTX *ctx; - PyObject *passphrase_callback, - *passphrase_userdata, - *verify_callback, - *info_callback, - *tlsext_servername_callback, - *app_data; - PyThreadState *tstate; -} ssl_ContextObj; - -#define ssl_SSLv2_METHOD (1) -#define ssl_SSLv3_METHOD (2) -#define ssl_SSLv23_METHOD (3) -#define ssl_TLSv1_METHOD (4) - - -#endif diff --git a/OpenSSL/ssl/ssl.c b/OpenSSL/ssl/ssl.c deleted file mode 100644 index 0dd9871..0000000 --- a/OpenSSL/ssl/ssl.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * ssl.c - * - * Copyright (C) AB Strakt - * Copyright (C) Jean-Paul Calderone - * See LICENSE for details. - * - * Main file of the SSL sub module. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - */ -#include - -#ifndef MS_WINDOWS -# include -# include -# if !(defined(__BEOS__) || defined(__CYGWIN__)) -# include -# endif -#else -# include -# include -#endif - -#define SSL_MODULE -#include "ssl.h" - -static char ssl_doc[] = "\n\ -Main file of the SSL sub module.\n\ -See the file RATIONALE for a short explanation of why this module was written.\n\ -"; - -crypto_X509Obj* (*new_x509)(X509*, int); -crypto_X509NameObj* (*new_x509name)(X509_NAME*, int); -crypto_X509StoreObj* (*new_x509store)(X509_STORE*, int); - - -#ifndef PY3 -void **crypto_API; -#endif - -int _pyOpenSSL_tstate_key; - -/* Exceptions defined by the SSL submodule */ -PyObject *ssl_Error, /* Base class */ - *ssl_ZeroReturnError, /* Used with SSL_get_error */ - *ssl_WantReadError, /* ... */ - *ssl_WantWriteError, /* ... */ - *ssl_WantX509LookupError, /* ... */ - *ssl_SysCallError; /* Uses (errno,errstr) */ - -static char ssl_SSLeay_version_doc[] = "\n\ -Return a string describing the version of OpenSSL in use.\n\ -\n\ -@param type: One of the SSLEAY_ constants defined in this module.\n\ -"; - -static PyObject * -ssl_SSLeay_version(PyObject *spam, PyObject *args) { - int t; - const char *version; - - if (!PyArg_ParseTuple(args, "i:SSLeay_version", &t)) { - return NULL; - } - - version = SSLeay_version(t); - return PyBytes_FromStringAndSize(version, strlen(version)); -} - - - -/* Methods in the OpenSSL.SSL module */ -static PyMethodDef ssl_methods[] = { - { "SSLeay_version", ssl_SSLeay_version, METH_VARARGS, ssl_SSLeay_version_doc }, - { NULL, NULL } -}; - -#ifdef PY3 -static struct PyModuleDef sslmodule = { - PyModuleDef_HEAD_INIT, - "SSL", - ssl_doc, - -1, - ssl_methods -}; -#endif - -/* - * Initialize SSL sub module - * - * Arguments: None - * Returns: None - */ -PyOpenSSL_MODINIT(SSL) { - PyObject *module; -#ifndef PY3 - static void *ssl_API[ssl_API_pointers]; - PyObject *ssl_api_object; - - import_crypto(); - - new_x509 = crypto_X509_New; - new_x509name = crypto_X509Name_New; - new_x509store = crypto_X509Store_New; -#else -# ifdef _WIN32 - HMODULE crypto = GetModuleHandle("crypto.pyd"); - if (crypto == NULL) { - PyErr_SetString(PyExc_RuntimeError, "Unable to get crypto module"); - PyOpenSSL_MODRETURN(NULL); - } - - new_x509 = (crypto_X509Obj* (*)(X509*, int))GetProcAddress(crypto, "crypto_X509_New"); - new_x509name = (crypto_X509NameObj* (*)(X509_NAME*, int))GetProcAddress(crypto, "crypto_X509Name_New"); - new_x509store = (crypto_X509StoreObj* (*)(X509_STORE*, int))GetProcAddress(crypto, "crypto_X509Store_New"); -# else - new_x509 = crypto_X509_New; - new_x509name = crypto_X509Name_New; - new_x509store = crypto_X509Store_New; -# endif -#endif - - SSL_library_init(); - ERR_load_SSL_strings(); - -#ifdef PY3 - module = PyModule_Create(&sslmodule); -#else - module = Py_InitModule3("SSL", ssl_methods, ssl_doc); -#endif - if (module == NULL) { - PyOpenSSL_MODRETURN(NULL); - } - -#ifndef PY3 - /* Initialize the C API pointer array */ - ssl_API[ssl_Context_New_NUM] = (void *)ssl_Context_New; - ssl_API[ssl_Connection_New_NUM] = (void *)ssl_Connection_New; - ssl_api_object = PyCObject_FromVoidPtr((void *)ssl_API, NULL); - if (ssl_api_object != NULL) { - /* PyModule_AddObject steals a reference. - */ - Py_INCREF(ssl_api_object); - PyModule_AddObject(module, "_C_API", ssl_api_object); - } -#endif - - /* Exceptions */ -/* - * ADD_EXCEPTION(dict,name,base) expands to a correct Exception declaration, - * inserting OpenSSL.SSL.name into dict, derviving the exception from base. - */ -#define ADD_EXCEPTION(_name, _base) \ -do { \ - ssl_##_name = PyErr_NewException("OpenSSL.SSL."#_name, _base, NULL);\ - if (ssl_##_name == NULL) \ - goto error; \ - /* PyModule_AddObject steals a reference. */ \ - Py_INCREF(ssl_##_name); \ - if (PyModule_AddObject(module, #_name, ssl_##_name) != 0) \ - goto error; \ -} while (0) - - ssl_Error = PyErr_NewException("OpenSSL.SSL.Error", NULL, NULL); - if (ssl_Error == NULL) { - goto error; - } - - /* PyModule_AddObject steals a reference. */ - Py_INCREF(ssl_Error); - if (PyModule_AddObject(module, "Error", ssl_Error) != 0) - goto error; - - ADD_EXCEPTION(ZeroReturnError, ssl_Error); - ADD_EXCEPTION(WantReadError, ssl_Error); - ADD_EXCEPTION(WantWriteError, ssl_Error); - ADD_EXCEPTION(WantX509LookupError, ssl_Error); - ADD_EXCEPTION(SysCallError, ssl_Error); -#undef ADD_EXCEPTION - - /* Method constants */ - PyModule_AddIntConstant(module, "SSLv2_METHOD", ssl_SSLv2_METHOD); - PyModule_AddIntConstant(module, "SSLv3_METHOD", ssl_SSLv3_METHOD); - PyModule_AddIntConstant(module, "SSLv23_METHOD", ssl_SSLv23_METHOD); - PyModule_AddIntConstant(module, "TLSv1_METHOD", ssl_TLSv1_METHOD); - - /* Verify constants */ - PyModule_AddIntConstant(module, "VERIFY_NONE", SSL_VERIFY_NONE); - PyModule_AddIntConstant(module, "VERIFY_PEER", SSL_VERIFY_PEER); - PyModule_AddIntConstant(module, "VERIFY_FAIL_IF_NO_PEER_CERT", - SSL_VERIFY_FAIL_IF_NO_PEER_CERT); - PyModule_AddIntConstant(module, "VERIFY_CLIENT_ONCE", - SSL_VERIFY_CLIENT_ONCE); - - /* File type constants */ - PyModule_AddIntConstant(module, "FILETYPE_PEM", SSL_FILETYPE_PEM); - PyModule_AddIntConstant(module, "FILETYPE_ASN1", SSL_FILETYPE_ASN1); - - /* SSL option constants */ - PyModule_AddIntConstant(module, "OP_SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE); - PyModule_AddIntConstant(module, "OP_EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA); - PyModule_AddIntConstant(module, "OP_NO_SSLv2", SSL_OP_NO_SSLv2); - PyModule_AddIntConstant(module, "OP_NO_SSLv3", SSL_OP_NO_SSLv3); - PyModule_AddIntConstant(module, "OP_NO_TLSv1", SSL_OP_NO_TLSv1); - - /* More SSL option constants */ - PyModule_AddIntConstant(module, "OP_MICROSOFT_SESS_ID_BUG", SSL_OP_MICROSOFT_SESS_ID_BUG); - PyModule_AddIntConstant(module, "OP_NETSCAPE_CHALLENGE_BUG", SSL_OP_NETSCAPE_CHALLENGE_BUG); - PyModule_AddIntConstant(module, "OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG); - PyModule_AddIntConstant(module, "OP_SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG); - PyModule_AddIntConstant(module, "OP_MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); - PyModule_AddIntConstant(module, "OP_MSIE_SSLV2_RSA_PADDING", SSL_OP_MSIE_SSLV2_RSA_PADDING); - PyModule_AddIntConstant(module, "OP_SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG); - PyModule_AddIntConstant(module, "OP_TLS_D5_BUG", SSL_OP_TLS_D5_BUG); - PyModule_AddIntConstant(module, "OP_TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG); - PyModule_AddIntConstant(module, "OP_DONT_INSERT_EMPTY_FRAGMENTS", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); - PyModule_AddIntConstant(module, "OP_ALL", SSL_OP_ALL); - PyModule_AddIntConstant(module, "OP_CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE); - PyModule_AddIntConstant(module, "OP_TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG); - PyModule_AddIntConstant(module, "OP_PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1); - PyModule_AddIntConstant(module, "OP_PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2); - PyModule_AddIntConstant(module, "OP_NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG); - PyModule_AddIntConstant(module, "OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG); - - /* DTLS related options. The first two of these were introduced in - * 2005, the third in 2007. To accomodate systems which are still using - * older versions, make them optional. */ -#ifdef SSL_OP_NO_QUERY_MTU - PyModule_AddIntConstant(module, "OP_NO_QUERY_MTU", SSL_OP_NO_QUERY_MTU); -#endif -#ifdef SSL_OP_COOKIE_EXCHANGE - PyModule_AddIntConstant(module, "OP_COOKIE_EXCHANGE", SSL_OP_COOKIE_EXCHANGE); -#endif -#ifdef SSL_OP_NO_TICKET - PyModule_AddIntConstant(module, "OP_NO_TICKET", SSL_OP_NO_TICKET); -#endif - - /* For SSL_set_shutdown */ - PyModule_AddIntConstant(module, "SENT_SHUTDOWN", SSL_SENT_SHUTDOWN); - PyModule_AddIntConstant(module, "RECEIVED_SHUTDOWN", SSL_RECEIVED_SHUTDOWN); - - /* For set_info_callback */ - PyModule_AddIntConstant(module, "SSL_ST_CONNECT", SSL_ST_CONNECT); - PyModule_AddIntConstant(module, "SSL_ST_ACCEPT", SSL_ST_ACCEPT); - PyModule_AddIntConstant(module, "SSL_ST_MASK", SSL_ST_MASK); - PyModule_AddIntConstant(module, "SSL_ST_INIT", SSL_ST_INIT); - PyModule_AddIntConstant(module, "SSL_ST_BEFORE", SSL_ST_BEFORE); - PyModule_AddIntConstant(module, "SSL_ST_OK", SSL_ST_OK); - PyModule_AddIntConstant(module, "SSL_ST_RENEGOTIATE", SSL_ST_RENEGOTIATE); - PyModule_AddIntConstant(module, "SSL_CB_LOOP", SSL_CB_LOOP); - PyModule_AddIntConstant(module, "SSL_CB_EXIT", SSL_CB_EXIT); - PyModule_AddIntConstant(module, "SSL_CB_READ", SSL_CB_READ); - PyModule_AddIntConstant(module, "SSL_CB_WRITE", SSL_CB_WRITE); - PyModule_AddIntConstant(module, "SSL_CB_ALERT", SSL_CB_ALERT); - PyModule_AddIntConstant(module, "SSL_CB_READ_ALERT", SSL_CB_READ_ALERT); - PyModule_AddIntConstant(module, "SSL_CB_WRITE_ALERT", SSL_CB_WRITE_ALERT); - PyModule_AddIntConstant(module, "SSL_CB_ACCEPT_LOOP", SSL_CB_ACCEPT_LOOP); - PyModule_AddIntConstant(module, "SSL_CB_ACCEPT_EXIT", SSL_CB_ACCEPT_EXIT); - PyModule_AddIntConstant(module, "SSL_CB_CONNECT_LOOP", SSL_CB_CONNECT_LOOP); - PyModule_AddIntConstant(module, "SSL_CB_CONNECT_EXIT", SSL_CB_CONNECT_EXIT); - PyModule_AddIntConstant(module, "SSL_CB_HANDSHAKE_START", SSL_CB_HANDSHAKE_START); - PyModule_AddIntConstant(module, "SSL_CB_HANDSHAKE_DONE", SSL_CB_HANDSHAKE_DONE); - - /* Version information indicators, used with SSLeay_version */ - PyModule_AddIntConstant(module, "SSLEAY_VERSION", SSLEAY_VERSION); - PyModule_AddIntConstant(module, "SSLEAY_CFLAGS", SSLEAY_CFLAGS); - PyModule_AddIntConstant(module, "SSLEAY_BUILT_ON", SSLEAY_BUILT_ON); - PyModule_AddIntConstant(module, "SSLEAY_PLATFORM", SSLEAY_PLATFORM); - PyModule_AddIntConstant(module, "SSLEAY_DIR", SSLEAY_DIR); - - /* Straight up version number */ - PyModule_AddIntConstant(module, "OPENSSL_VERSION_NUMBER", OPENSSL_VERSION_NUMBER); - - if (!init_ssl_context(module)) - goto error; - if (!init_ssl_connection(module)) - goto error; - -#ifdef WITH_THREAD - /* - * Initialize this module's threading support structures. - */ - _pyOpenSSL_tstate_key = PyThread_create_key(); -#endif - - PyOpenSSL_MODRETURN(module); - -error: - PyOpenSSL_MODRETURN(NULL); - ; -} diff --git a/OpenSSL/ssl/ssl.h b/OpenSSL/ssl/ssl.h deleted file mode 100644 index 6a0a57e..0000000 --- a/OpenSSL/ssl/ssl.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * ssl.h - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * Export functions and exceptions from the SSL sub module. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - * - */ -#ifndef PyOpenSSL_SSL_H_ -#define PyOpenSSL_SSL_H_ - -#include -#include -#include "context.h" -#include "connection.h" -#include "../util.h" -#include "../crypto/crypto.h" - -extern PyObject *ssl_Error, /* Base class */ - *ssl_ZeroReturnError, /* Used with SSL_get_erorr */ - *ssl_WantReadError, /* ... */ - *ssl_WantWriteError, /* ... */ - *ssl_WantX509LookupError, /* ... */ - *ssl_SysCallError; /* Uses (errno,errstr) */ - -#define ssl_Context_New_NUM 0 -#define ssl_Context_New_RETURN ssl_ContextObj * -#define ssl_Context_New_PROTO (int method) - -#define ssl_Connection_New_NUM 1 -#define ssl_Connection_New_RETURN ssl_ConnectionObj * -#define ssl_Connection_New_PROTO (ssl_ContextObj *ctx, PyObject *sock) - -#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; -extern ssl_Connection_New_RETURN ssl_Connection_New ssl_Connection_New_PROTO; - -extern crypto_X509Obj* (*new_x509)(X509*, int); -extern crypto_X509NameObj* (*new_x509name)(X509_NAME*, int); -extern crypto_X509StoreObj* (*new_x509store)(X509_STORE*, int); - -#else /* SSL_MODULE */ - -extern void **ssl_API; - -#define ssl_Context_New \ - (*(ssl_Context_New_RETURN (*)ssl_Context_New_PROTO) ssl_API[ssl_Context_New_NUM]) -#define ssl_Connection_New \ - (*(ssl_Connection_New_RETURN (*)ssl_Connection_New_PROTO) ssl_API[ssl_Connection_New_NUM]) - -#define import_SSL() \ -{ \ - PyObject *module = PyImport_ImportModule("OpenSSL.SSL"); \ - if (module != NULL) { \ - PyObject *module_dict = PyModule_GetDict(module); \ - PyObject *c_api_object = PyDict_GetItemString(module_dict, "_C_API"); \ - if (PyCObject_Check(c_api_object)) { \ - ssl_API = (void **)PyCObject_AsVoidPtr(c_api_object); \ - } \ - } \ -} - -#endif /* SSL_MODULE */ - -#endif /* PyOpenSSL_SSL_H_ */ diff --git a/OpenSSL/test/README b/OpenSSL/test/README new file mode 100644 index 0000000..8b3b3ff --- /dev/null +++ b/OpenSSL/test/README @@ -0,0 +1,8 @@ +These tests are meant to be run using twisted's "trial" command. + +See http://twistedmatrix.com/trac/wiki/TwistedTrial + +For example... + +$ sudo python ./setup install +$ trial OpenSSL diff --git a/OpenSSL/test/__init__.py b/OpenSSL/test/__init__.py index ccb4e9a..9b08060 100644 --- a/OpenSSL/test/__init__.py +++ b/OpenSSL/test/__init__.py @@ -2,5 +2,5 @@ # See LICENSE for details. """ -Package containing unit tests for L{OpenSSL}. +Package containing unit tests for :py:mod:`OpenSSL`. """ diff --git a/OpenSSL/test/test_crypto.py b/OpenSSL/test/test_crypto.py index 5cdcefb..4e42f70 100644 --- a/OpenSSL/test/test_crypto.py +++ b/OpenSSL/test/test_crypto.py @@ -2,7 +2,7 @@ # See LICENSE file for details. """ -Unit tests for L{OpenSSL.crypto}. +Unit tests for :py:mod:`OpenSSL.crypto`. """ from unittest import main @@ -11,9 +11,11 @@ import os, re from subprocess import PIPE, Popen from datetime import datetime, timedelta +from six import binary_type + 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 X509Store, X509StoreType, X509Req, X509ReqType from OpenSSL.crypto import X509Extension, X509ExtensionType from OpenSSL.crypto import load_certificate, load_privatekey from OpenSSL.crypto import FILETYPE_PEM, FILETYPE_ASN1, FILETYPE_TEXT @@ -24,7 +26,8 @@ from OpenSSL.crypto import PKCS12, PKCS12Type, load_pkcs12 from OpenSSL.crypto import CRL, Revoked, load_crl from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType from OpenSSL.crypto import sign, verify -from OpenSSL.test.util import TestCase, bytes, b +from OpenSSL.test.util import TestCase, b +from OpenSSL._util import native def normalize_certificate_pem(pem): return dump_certificate(FILETYPE_PEM, load_certificate(FILETYPE_PEM, pem)) @@ -34,6 +37,12 @@ def normalize_privatekey_pem(pem): return dump_privatekey(FILETYPE_PEM, load_privatekey(FILETYPE_PEM, pem)) +GOOD_CIPHER = "blowfish" +BAD_CIPHER = "zippers" + +GOOD_DIGEST = "MD5" +BAD_DIGEST = "monkeys" + root_cert_pem = b("""-----BEGIN CERTIFICATE----- MIIC7TCCAlagAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU @@ -265,10 +274,41 @@ oolb6NMg/R3enNPvS1O4UU1H8wpaF77L4yiSWlE0p4w= -----END RSA PRIVATE KEY----- """) +# certificate with NULL bytes in subjectAltName and common name + +nulbyteSubjectAltNamePEM = b("""-----BEGIN CERTIFICATE----- +MIIE2DCCA8CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBxTELMAkGA1UEBhMCVVMx +DzANBgNVBAgMBk9yZWdvbjESMBAGA1UEBwwJQmVhdmVydG9uMSMwIQYDVQQKDBpQ +eXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEgMB4GA1UECwwXUHl0aG9uIENvcmUg +RGV2ZWxvcG1lbnQxJDAiBgNVBAMMG251bGwucHl0aG9uLm9yZwBleGFtcGxlLm9y +ZzEkMCIGCSqGSIb3DQEJARYVcHl0aG9uLWRldkBweXRob24ub3JnMB4XDTEzMDgw +NzEzMTE1MloXDTEzMDgwNzEzMTI1MlowgcUxCzAJBgNVBAYTAlVTMQ8wDQYDVQQI +DAZPcmVnb24xEjAQBgNVBAcMCUJlYXZlcnRvbjEjMCEGA1UECgwaUHl0aG9uIFNv +ZnR3YXJlIEZvdW5kYXRpb24xIDAeBgNVBAsMF1B5dGhvbiBDb3JlIERldmVsb3Bt +ZW50MSQwIgYDVQQDDBtudWxsLnB5dGhvbi5vcmcAZXhhbXBsZS5vcmcxJDAiBgkq +hkiG9w0BCQEWFXB5dGhvbi1kZXZAcHl0aG9uLm9yZzCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBALXq7cn7Rn1vO3aA3TrzA5QLp6bb7B3f/yN0CJ2XFj+j +pHs+Gw6WWSUDpybiiKnPec33BFawq3kyblnBMjBU61ioy5HwQqVkJ8vUVjGIUq3P +vX/wBmQfzCe4o4uM89gpHyUL9UYGG8oCRa17dgqcv7u5rg0Wq2B1rgY+nHwx3JIv +KRrgSwyRkGzpN8WQ1yrXlxWjgI9de0mPVDDUlywcWze1q2kwaEPTM3hLAmD1PESA +oY/n8A/RXoeeRs9i/Pm/DGUS8ZPINXk/yOzsR/XvvkTVroIeLZqfmFpnZeF0cHzL +08LODkVJJ9zjLdT7SA4vnne4FEbAxDbKAq5qkYzaL4UCAwEAAaOB0DCBzTAMBgNV +HRMBAf8EAjAAMB0GA1UdDgQWBBSIWlXAUv9hzVKjNQ/qWpwkOCL3XDALBgNVHQ8E +BAMCBeAwgZAGA1UdEQSBiDCBhYIeYWx0bnVsbC5weXRob24ub3JnAGV4YW1wbGUu +Y29tgSBudWxsQHB5dGhvbi5vcmcAdXNlckBleGFtcGxlLm9yZ4YpaHR0cDovL251 +bGwucHl0aG9uLm9yZwBodHRwOi8vZXhhbXBsZS5vcmeHBMAAAgGHECABDbgAAAAA +AAAAAAAAAAEwDQYJKoZIhvcNAQEFBQADggEBAKxPRe99SaghcI6IWT7UNkJw9aO9 +i9eo0Fj2MUqxpKbdb9noRDy2CnHWf7EIYZ1gznXPdwzSN4YCjV5d+Q9xtBaowT0j +HPERs1ZuytCNNJTmhyqZ8q6uzMLoht4IqH/FBfpvgaeC5tBTnTT0rD5A/olXeimk +kX4LxlEx5RAvpGB2zZVRGr6LobD9rVK91xuHYNIxxxfEGE8tCCWjp0+3ksri9SXx +VHWBnbM9YaL32u3hxm8sYB/Yb8WSBavJCWJJqRStVRHM1koZlJmXNx2BX4vPo6iW +RFEIPQsFZRLrtnCAiEhyT8bC2s/Njlu6ly9gtJZWSV46Q3ZjBL4q9sHKqZQ= +-----END CERTIFICATE-----""") + class X509ExtTests(TestCase): """ - Tests for L{OpenSSL.crypto.X509Extension}. + Tests for :py:class:`OpenSSL.crypto.X509Extension`. """ def setUp(self): @@ -276,6 +316,7 @@ class X509ExtTests(TestCase): Create a new private key and start a certificate request (for a test method to finish in one way or another). """ + super(X509ExtTests, self).setUp() # Basic setup stuff to generate a certificate self.pkey = PKey() self.pkey.generate_key(TYPE_RSA, 384) @@ -294,10 +335,19 @@ class X509ExtTests(TestCase): self.x509.set_notAfter(expire) + def tearDown(self): + """ + Forget all of the pyOpenSSL objects so they can be garbage collected, + their memory released, and not interfere with the leak detection code. + """ + self.pkey = self.req = self.x509 = self.subject = None + super(X509ExtTests, self).tearDown() + + def test_str(self): """ - The string representation of L{X509Extension} instances as returned by - C{str} includes stuff. + The string representation of :py:class:`X509Extension` instances as returned by + :py:data:`str` includes stuff. """ # This isn't necessarily the best string representation. Perhaps it # will be changed/improved in the future. @@ -308,7 +358,7 @@ class X509ExtTests(TestCase): def test_type(self): """ - L{X509Extension} and L{X509ExtensionType} refer to the same type object + :py:class:`X509Extension` and :py:class:`X509ExtensionType` refer to the same type object and can be used to create instances of that type. """ self.assertIdentical(X509Extension, X509ExtensionType) @@ -319,8 +369,8 @@ class X509ExtTests(TestCase): def test_construction(self): """ - L{X509Extension} accepts an extension type name, a critical flag, - and an extension value and returns an L{X509ExtensionType} instance. + :py:class:`X509Extension` accepts an extension type name, a critical flag, + and an extension value and returns an :py:class:`X509ExtensionType` instance. """ basic = X509Extension(b('basicConstraints'), True, b('CA:true')) self.assertTrue( @@ -338,7 +388,7 @@ class X509ExtTests(TestCase): def test_invalid_extension(self): """ - L{X509Extension} raises something if it is passed a bad extension + :py:class:`X509Extension` raises something if it is passed a bad extension name or value. """ self.assertRaises( @@ -357,7 +407,7 @@ class X509ExtTests(TestCase): def test_get_critical(self): """ - L{X509ExtensionType.get_critical} returns the value of the + :py:meth:`X509ExtensionType.get_critical` returns the value of the extension's critical flag. """ ext = X509Extension(b('basicConstraints'), True, b('CA:true')) @@ -368,7 +418,7 @@ class X509ExtTests(TestCase): def test_get_short_name(self): """ - L{X509ExtensionType.get_short_name} returns a string giving the short + :py:meth:`X509ExtensionType.get_short_name` returns a string giving the short type name of the extension. """ ext = X509Extension(b('basicConstraints'), True, b('CA:true')) @@ -379,7 +429,7 @@ class X509ExtTests(TestCase): def test_get_data(self): """ - L{X509Extension.get_data} returns a string giving the data of the + :py:meth:`X509Extension.get_data` returns a string giving the data of the extension. """ ext = X509Extension(b('basicConstraints'), True, b('CA:true')) @@ -389,7 +439,7 @@ class X509ExtTests(TestCase): def test_get_data_wrong_args(self): """ - L{X509Extension.get_data} raises L{TypeError} if passed any arguments. + :py:meth:`X509Extension.get_data` raises :py:exc:`TypeError` if passed any arguments. """ ext = X509Extension(b('basicConstraints'), True, b('CA:true')) self.assertRaises(TypeError, ext.get_data, None) @@ -399,7 +449,7 @@ class X509ExtTests(TestCase): def test_unused_subject(self): """ - The C{subject} parameter to L{X509Extension} may be provided for an + The :py:data:`subject` parameter to :py:class:`X509Extension` may be provided for an extension which does not use it and is ignored in this case. """ ext1 = X509Extension( @@ -414,8 +464,8 @@ class X509ExtTests(TestCase): def test_subject(self): """ - If an extension requires a subject, the C{subject} parameter to - L{X509Extension} provides its value. + If an extension requires a subject, the :py:data:`subject` parameter to + :py:class:`X509Extension` provides its value. """ ext3 = X509Extension( b('subjectKeyIdentifier'), False, b('hash'), subject=self.x509) @@ -427,7 +477,7 @@ class X509ExtTests(TestCase): def test_missing_subject(self): """ - If an extension requires a subject and the C{subject} parameter is + If an extension requires a subject and the :py:data:`subject` parameter is given no value, something happens. """ self.assertRaises( @@ -436,8 +486,8 @@ class X509ExtTests(TestCase): def test_invalid_subject(self): """ - If the C{subject} parameter is given a value which is not an L{X509} - instance, L{TypeError} is raised. + If the :py:data:`subject` parameter is given a value which is not an + :py:class:`X509` instance, :py:exc:`TypeError` is raised. """ for badObj in [True, object(), "hello", [], self]: self.assertRaises( @@ -448,7 +498,7 @@ class X509ExtTests(TestCase): def test_unused_issuer(self): """ - The C{issuer} parameter to L{X509Extension} may be provided for an + The :py:data:`issuer` parameter to :py:class:`X509Extension` may be provided for an extension which does not use it and is ignored in this case. """ ext1 = X509Extension( @@ -462,8 +512,8 @@ class X509ExtTests(TestCase): def test_issuer(self): """ - If an extension requires a issuer, the C{issuer} parameter to - L{X509Extension} provides its value. + If an extension requires a issuer, the :py:data:`issuer` parameter to + :py:class:`X509Extension` provides its value. """ ext2 = X509Extension( b('authorityKeyIdentifier'), False, b('issuer:always'), @@ -477,7 +527,7 @@ class X509ExtTests(TestCase): def test_missing_issuer(self): """ - If an extension requires an issue and the C{issuer} parameter is given + If an extension requires an issue and the :py:data:`issuer` parameter is given no value, something happens. """ self.assertRaises( @@ -489,8 +539,8 @@ class X509ExtTests(TestCase): def test_invalid_issuer(self): """ - If the C{issuer} parameter is given a value which is not an L{X509} - instance, L{TypeError} is raised. + If the :py:data:`issuer` parameter is given a value which is not an + :py:class:`X509` instance, :py:exc:`TypeError` is raised. """ for badObj in [True, object(), "hello", [], self]: self.assertRaises( @@ -503,12 +553,12 @@ class X509ExtTests(TestCase): class PKeyTests(TestCase): """ - Unit tests for L{OpenSSL.crypto.PKey}. + Unit tests for :py:class:`OpenSSL.crypto.PKey`. """ def test_type(self): """ - L{PKey} and L{PKeyType} refer to the same type object and can be used - to create instances of that type. + :py:class:`PKey` and :py:class:`PKeyType` refer to the same type object + and can be used to create instances of that type. """ self.assertIdentical(PKey, PKeyType) self.assertConsistentType(PKey, 'PKey') @@ -516,7 +566,7 @@ class PKeyTests(TestCase): def test_construction(self): """ - L{PKey} takes no arguments and returns a new L{PKey} instance. + :py:class:`PKey` takes no arguments and returns a new :py:class:`PKey` instance. """ self.assertRaises(TypeError, PKey, None) key = PKey() @@ -527,8 +577,8 @@ class PKeyTests(TestCase): def test_pregeneration(self): """ - L{PKeyType.bits} and L{PKeyType.type} return C{0} before the key is - generated. L{PKeyType.check} raises L{TypeError} before the key is + :py:attr:`PKeyType.bits` and :py:attr:`PKeyType.type` return :py:data:`0` before the key is + generated. :py:attr:`PKeyType.check` raises :py:exc:`TypeError` before the key is generated. """ key = PKey() @@ -539,11 +589,11 @@ class PKeyTests(TestCase): def test_failedGeneration(self): """ - L{PKeyType.generate_key} takes two arguments, the first giving the key - type as one of L{TYPE_RSA} or L{TYPE_DSA} and the second giving the + :py:meth:`PKeyType.generate_key` takes two arguments, the first giving the key + type as one of :py:data:`TYPE_RSA` or :py:data:`TYPE_DSA` and the second giving the number of bits to generate. If an invalid type is specified or - generation fails, L{Error} is raised. If an invalid number of bits is - specified, L{ValueError} or L{Error} is raised. + generation fails, :py:exc:`Error` is raised. If an invalid number of bits is + specified, :py:exc:`ValueError` or :py:exc:`Error` is raised. """ key = PKey() self.assertRaises(TypeError, key.generate_key) @@ -574,8 +624,8 @@ class PKeyTests(TestCase): def test_rsaGeneration(self): """ - L{PKeyType.generate_key} generates an RSA key when passed - L{TYPE_RSA} as a type and a reasonable number of bits. + :py:meth:`PKeyType.generate_key` generates an RSA key when passed + :py:data:`TYPE_RSA` as a type and a reasonable number of bits. """ bits = 128 key = PKey() @@ -587,8 +637,8 @@ class PKeyTests(TestCase): def test_dsaGeneration(self): """ - L{PKeyType.generate_key} generates a DSA key when passed - L{TYPE_DSA} as a type and a reasonable number of bits. + :py:meth:`PKeyType.generate_key` generates a DSA key when passed + :py:data:`TYPE_DSA` as a type and a reasonable number of bits. """ # 512 is a magic number. The DSS (Digital Signature Standard) # allows a minimum of 512 bits for DSA. DSA_generate_parameters @@ -596,14 +646,14 @@ class PKeyTests(TestCase): bits = 512 key = PKey() key.generate_key(TYPE_DSA, bits) - self.assertEqual(key.type(), TYPE_DSA) - self.assertEqual(key.bits(), bits) - self.assertRaises(TypeError, key.check) + # self.assertEqual(key.type(), TYPE_DSA) + # self.assertEqual(key.bits(), bits) + # self.assertRaises(TypeError, key.check) def test_regeneration(self): """ - L{PKeyType.generate_key} can be called multiple times on the same + :py:meth:`PKeyType.generate_key` can be called multiple times on the same key to generate new keys. """ key = PKey() @@ -615,7 +665,7 @@ class PKeyTests(TestCase): def test_inconsistentKey(self): """ - L{PKeyType.check} returns C{False} if the key is not consistent. + :py:`PKeyType.check` returns :py:exc:`Error` if the key is not consistent. """ key = load_privatekey(FILETYPE_PEM, inconsistentPrivateKeyPEM) self.assertRaises(Error, key.check) @@ -623,17 +673,31 @@ class PKeyTests(TestCase): def test_check_wrong_args(self): """ - L{PKeyType.check} raises L{TypeError} if called with any arguments. + :py:meth:`PKeyType.check` raises :py:exc:`TypeError` if called with any arguments. """ self.assertRaises(TypeError, PKey().check, None) self.assertRaises(TypeError, PKey().check, object()) self.assertRaises(TypeError, PKey().check, 1) + def test_check_public_key(self): + """ + :py:meth:`PKeyType.check` raises :py:exc:`TypeError` if only the public + part of the key is available. + """ + # A trick to get a public-only key + key = PKey() + key.generate_key(TYPE_RSA, 512) + cert = X509() + cert.set_pubkey(key) + pub = cert.get_pubkey() + self.assertRaises(TypeError, pub.check) + + class X509NameTests(TestCase): """ - Unit tests for L{OpenSSL.crypto.X509Name}. + Unit tests for :py:class:`OpenSSL.crypto.X509Name`. """ def _x509name(self, **attrs): # XXX There's no other way to get a new X509Name yet. @@ -650,7 +714,7 @@ class X509NameTests(TestCase): def test_type(self): """ - The type of X509Name objects is L{X509NameType}. + The type of X509Name objects is :py:class:`X509NameType`. """ self.assertIdentical(X509Name, X509NameType) self.assertEqual(X509NameType.__name__, 'X509Name') @@ -665,25 +729,28 @@ class X509NameTests(TestCase): def test_onlyStringAttributes(self): """ - Attempting to set a non-L{str} attribute name on an L{X509NameType} - instance causes L{TypeError} to be raised. + Attempting to set a non-:py:data:`str` attribute name on an :py:class:`X509NameType` + instance causes :py:exc:`TypeError` to be raised. """ name = self._x509name() # Beyond these cases, you may also think that unicode should be # rejected. Sorry, you're wrong. unicode is automatically converted to # str outside of the control of X509Name, so there's no way to reject # it. + + # Also, this used to test str subclasses, but that test is less relevant + # now that the implementation is in Python instead of C. Also PyPy + # automatically converts str subclasses to str when they are passed to + # setattr, so we can't test it on PyPy. Apparently CPython does this + # sometimes as well. self.assertRaises(TypeError, setattr, name, None, "hello") self.assertRaises(TypeError, setattr, name, 30, "hello") - class evil(str): - pass - self.assertRaises(TypeError, setattr, name, evil(), "hello") def test_setInvalidAttribute(self): """ - Attempting to set any attribute name on an L{X509NameType} instance for - which no corresponding NID is defined causes L{AttributeError} to be + Attempting to set any attribute name on an :py:class:`X509NameType` instance for + which no corresponding NID is defined causes :py:exc:`AttributeError` to be raised. """ name = self._x509name() @@ -692,7 +759,7 @@ class X509NameTests(TestCase): def test_attributes(self): """ - L{X509NameType} instances have attributes for each standard (?) + :py:class:`X509NameType` instances have attributes for each standard (?) X509Name field. """ name = self._x509name() @@ -712,8 +779,8 @@ class X509NameTests(TestCase): def test_copy(self): """ - L{X509Name} creates a new L{X509NameType} instance with all the same - attributes as an existing L{X509NameType} instance when called with + :py:class:`X509Name` creates a new :py:class:`X509NameType` instance with all the same + attributes as an existing :py:class:`X509NameType` instance when called with one. """ name = self._x509name(commonName="foo", emailAddress="bar@example.com") @@ -733,7 +800,7 @@ class X509NameTests(TestCase): def test_repr(self): """ - L{repr} passed an L{X509NameType} instance should return a string + :py:func:`repr` passed an :py:class:`X509NameType` instance should return a string containing a description of the type and the NIDs which have been set on it. """ @@ -745,7 +812,7 @@ class X509NameTests(TestCase): def test_comparison(self): """ - L{X509NameType} instances should compare based on their NIDs. + :py:class:`X509NameType` instances should compare based on their NIDs. """ def _equality(a, b, assertTrue, assertFalse): assertTrue(a == b, "(%r == %r) --> False" % (a, b)) @@ -820,7 +887,7 @@ class X509NameTests(TestCase): def test_hash(self): """ - L{X509Name.hash} returns an integer hash based on the value of the + :py:meth:`X509Name.hash` returns an integer hash based on the value of the name. """ a = self._x509name(CN="foo") @@ -832,7 +899,7 @@ class X509NameTests(TestCase): def test_der(self): """ - L{X509Name.der} returns the DER encoded form of the name. + :py:meth:`X509Name.der` returns the DER encoded form of the name. """ a = self._x509name(CN="foo", C="US") self.assertEqual( @@ -843,7 +910,8 @@ class X509NameTests(TestCase): def test_get_components(self): """ - L{X509Name.get_components} returns a C{list} of two-tuples of C{str} + :py:meth:`X509Name.get_components` returns a :py:data:`list` of + two-tuples of :py:data:`str` giving the NIDs and associated values which make up the name. """ a = self._x509name() @@ -856,60 +924,85 @@ class X509NameTests(TestCase): [(b("CN"), b("foo")), (b("OU"), b("bar"))]) + def test_load_nul_byte_attribute(self): + """ + An :py:class:`OpenSSL.crypto.X509Name` from an + :py:class:`OpenSSL.crypto.X509` instance loaded from a file can have a + NUL byte in the value of one of its attributes. + """ + cert = load_certificate(FILETYPE_PEM, nulbyteSubjectAltNamePEM) + subject = cert.get_subject() + self.assertEqual( + "null.python.org\x00example.org", subject.commonName) + + + def test_setAttributeFailure(self): + """ + If the value of an attribute cannot be set for some reason then + :py:class:`OpenSSL.crypto.Error` is raised. + """ + name = self._x509name() + # This value is too long + self.assertRaises(Error, setattr, name, "O", b"x" * 512) + + + class _PKeyInteractionTestsMixin: """ Tests which involve another thing and a PKey. """ def signable(self): """ - Return something with a C{set_pubkey}, C{set_pubkey}, and C{sign} method. + Return something with a :py:meth:`set_pubkey`, :py:meth:`set_pubkey`, + and :py:meth:`sign` method. """ raise NotImplementedError() def test_signWithUngenerated(self): """ - L{X509Req.sign} raises L{ValueError} when pass a L{PKey} with no parts. + :py:meth:`X509Req.sign` raises :py:exc:`ValueError` when pass a + :py:class:`PKey` with no parts. """ request = self.signable() key = PKey() - self.assertRaises(ValueError, request.sign, key, 'MD5') + self.assertRaises(ValueError, request.sign, key, GOOD_DIGEST) def test_signWithPublicKey(self): """ - L{X509Req.sign} raises L{ValueError} when pass a L{PKey} with no - private part as the signing key. + :py:meth:`X509Req.sign` raises :py:exc:`ValueError` when pass a + :py:class:`PKey` with no private part as the signing key. """ request = self.signable() key = PKey() key.generate_key(TYPE_RSA, 512) request.set_pubkey(key) pub = request.get_pubkey() - self.assertRaises(ValueError, request.sign, pub, 'MD5') + self.assertRaises(ValueError, request.sign, pub, GOOD_DIGEST) def test_signWithUnknownDigest(self): """ - L{X509Req.sign} raises L{ValueError} when passed a digest name which is + :py:meth:`X509Req.sign` raises :py:exc:`ValueError` when passed a digest name which is not known. """ request = self.signable() key = PKey() key.generate_key(TYPE_RSA, 512) - self.assertRaises(ValueError, request.sign, key, "monkeys") + self.assertRaises(ValueError, request.sign, key, BAD_DIGEST) def test_sign(self): """ - L{X509Req.sign} succeeds when passed a private key object and a valid - digest function. C{X509Req.verify} can be used to check the signature. + :py:meth:`X509Req.sign` succeeds when passed a private key object and a valid + digest function. :py:meth:`X509Req.verify` can be used to check the signature. """ request = self.signable() key = PKey() key.generate_key(TYPE_RSA, 512) request.set_pubkey(key) - request.sign(key, 'MD5') + request.sign(key, GOOD_DIGEST) # If the type has a verify method, cover that too. if getattr(request, 'verify', None) is not None: pub = request.get_pubkey() @@ -924,18 +1017,18 @@ class _PKeyInteractionTestsMixin: class X509ReqTests(TestCase, _PKeyInteractionTestsMixin): """ - Tests for L{OpenSSL.crypto.X509Req}. + Tests for :py:class:`OpenSSL.crypto.X509Req`. """ def signable(self): """ - Create and return a new L{X509Req}. + Create and return a new :py:class:`X509Req`. """ return X509Req() def test_type(self): """ - L{X509Req} and L{X509ReqType} refer to the same type object and can be + :py:obj:`X509Req` and :py:obj:`X509ReqType` refer to the same type object and can be used to create instances of that type. """ self.assertIdentical(X509Req, X509ReqType) @@ -944,7 +1037,7 @@ class X509ReqTests(TestCase, _PKeyInteractionTestsMixin): def test_construction(self): """ - L{X509Req} takes no arguments and returns an L{X509ReqType} instance. + :py:obj:`X509Req` takes no arguments and returns an :py:obj:`X509ReqType` instance. """ request = X509Req() self.assertTrue( @@ -954,8 +1047,8 @@ class X509ReqTests(TestCase, _PKeyInteractionTestsMixin): def test_version(self): """ - L{X509ReqType.set_version} sets the X.509 version of the certificate - request. L{X509ReqType.get_version} returns the X.509 version of + :py:obj:`X509ReqType.set_version` sets the X.509 version of the certificate + request. :py:obj:`X509ReqType.get_version` returns the X.509 version of the certificate request. The initial value of the version is 0. """ request = X509Req() @@ -968,9 +1061,9 @@ class X509ReqTests(TestCase, _PKeyInteractionTestsMixin): def test_version_wrong_args(self): """ - L{X509ReqType.set_version} raises L{TypeError} if called with the wrong - number of arguments or with a non-C{int} argument. - L{X509ReqType.get_version} raises L{TypeError} if called with any + :py:obj:`X509ReqType.set_version` raises :py:obj:`TypeError` if called with the wrong + number of arguments or with a non-:py:obj:`int` argument. + :py:obj:`X509ReqType.get_version` raises :py:obj:`TypeError` if called with any arguments. """ request = X509Req() @@ -982,7 +1075,7 @@ class X509ReqTests(TestCase, _PKeyInteractionTestsMixin): def test_get_subject(self): """ - L{X509ReqType.get_subject} returns an L{X509Name} for the subject of + :py:obj:`X509ReqType.get_subject` returns an :py:obj:`X509Name` for the subject of the request and which is valid even after the request object is otherwise dead. """ @@ -1000,7 +1093,7 @@ class X509ReqTests(TestCase, _PKeyInteractionTestsMixin): def test_get_subject_wrong_args(self): """ - L{X509ReqType.get_subject} raises L{TypeError} if called with any + :py:obj:`X509ReqType.get_subject` raises :py:obj:`TypeError` if called with any arguments. """ request = X509Req() @@ -1009,7 +1102,7 @@ class X509ReqTests(TestCase, _PKeyInteractionTestsMixin): def test_add_extensions(self): """ - L{X509Req.add_extensions} accepts a C{list} of L{X509Extension} + :py:obj:`X509Req.add_extensions` accepts a :py:obj:`list` of :py:obj:`X509Extension` instances and adds them to the X509 request. """ request = X509Req() @@ -1020,9 +1113,9 @@ class X509ReqTests(TestCase, _PKeyInteractionTestsMixin): def test_add_extensions_wrong_args(self): """ - L{X509Req.add_extensions} raises L{TypeError} if called with the wrong - number of arguments or with a non-C{list}. Or it raises L{ValueError} - if called with a C{list} containing objects other than L{X509Extension} + :py:obj:`X509Req.add_extensions` raises :py:obj:`TypeError` if called with the wrong + number of arguments or with a non-:py:obj:`list`. Or it raises :py:obj:`ValueError` + if called with a :py:obj:`list` containing objects other than :py:obj:`X509Extension` instances. """ request = X509Req() @@ -1032,10 +1125,57 @@ class X509ReqTests(TestCase, _PKeyInteractionTestsMixin): self.assertRaises(TypeError, request.add_extensions, [], None) + def test_verify_wrong_args(self): + """ + :py:obj:`X509Req.verify` raises :py:obj:`TypeError` if called with zero + arguments or more than one argument or if passed anything other than a + :py:obj:`PKey` instance as its single argument. + """ + request = X509Req() + self.assertRaises(TypeError, request.verify) + self.assertRaises(TypeError, request.verify, object()) + self.assertRaises(TypeError, request.verify, PKey(), object()) + + + def test_verify_uninitialized_key(self): + """ + :py:obj:`X509Req.verify` raises :py:obj:`OpenSSL.crypto.Error` if called + with a :py:obj:`OpenSSL.crypto.PKey` which contains no key data. + """ + request = X509Req() + pkey = PKey() + self.assertRaises(Error, request.verify, pkey) + + + def test_verify_wrong_key(self): + """ + :py:obj:`X509Req.verify` raises :py:obj:`OpenSSL.crypto.Error` if called + with a :py:obj:`OpenSSL.crypto.PKey` which does not represent the public + part of the key which signed the request. + """ + request = X509Req() + pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) + request.sign(pkey, GOOD_DIGEST) + another_pkey = load_privatekey(FILETYPE_PEM, client_key_pem) + self.assertRaises(Error, request.verify, another_pkey) + + + def test_verify_success(self): + """ + :py:obj:`X509Req.verify` returns :py:obj:`True` if called with a + :py:obj:`OpenSSL.crypto.PKey` which represents the public part ofthe key + which signed the request. + """ + request = X509Req() + pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) + request.sign(pkey, GOOD_DIGEST) + self.assertEqual(True, request.verify(pkey)) + + class X509Tests(TestCase, _PKeyInteractionTestsMixin): """ - Tests for L{OpenSSL.crypto.X509}. + Tests for :py:obj:`OpenSSL.crypto.X509`. """ pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM @@ -1061,14 +1201,14 @@ WpOdIpB8KksUTCzV591Nr1wd """ def signable(self): """ - Create and return a new L{X509}. + Create and return a new :py:obj:`X509`. """ return X509() def test_type(self): """ - L{X509} and L{X509Type} refer to the same type object and can be used + :py:obj:`X509` and :py:obj:`X509Type` refer to the same type object and can be used to create instances of that type. """ self.assertIdentical(X509, X509Type) @@ -1077,7 +1217,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_construction(self): """ - L{X509} takes no arguments and returns an instance of L{X509Type}. + :py:obj:`X509` takes no arguments and returns an instance of :py:obj:`X509Type`. """ certificate = X509() self.assertTrue( @@ -1093,7 +1233,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_get_version_wrong_args(self): """ - L{X509.get_version} raises L{TypeError} if invoked with any arguments. + :py:obj:`X509.get_version` raises :py:obj:`TypeError` if invoked with any arguments. """ cert = X509() self.assertRaises(TypeError, cert.get_version, None) @@ -1101,8 +1241,8 @@ WpOdIpB8KksUTCzV591Nr1wd def test_set_version_wrong_args(self): """ - L{X509.set_version} raises L{TypeError} if invoked with the wrong number - of arguments or an argument not of type C{int}. + :py:obj:`X509.set_version` raises :py:obj:`TypeError` if invoked with the wrong number + of arguments or an argument not of type :py:obj:`int`. """ cert = X509() self.assertRaises(TypeError, cert.set_version) @@ -1112,8 +1252,8 @@ WpOdIpB8KksUTCzV591Nr1wd def test_version(self): """ - L{X509.set_version} sets the certificate version number. - L{X509.get_version} retrieves it. + :py:obj:`X509.set_version` sets the certificate version number. + :py:obj:`X509.get_version` retrieves it. """ cert = X509() cert.set_version(1234) @@ -1122,7 +1262,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_get_serial_number_wrong_args(self): """ - L{X509.get_serial_number} raises L{TypeError} if invoked with any + :py:obj:`X509.get_serial_number` raises :py:obj:`TypeError` if invoked with any arguments. """ cert = X509() @@ -1131,8 +1271,8 @@ WpOdIpB8KksUTCzV591Nr1wd def test_serial_number(self): """ - The serial number of an L{X509Type} can be retrieved and modified with - L{X509Type.get_serial_number} and L{X509Type.set_serial_number}. + The serial number of an :py:obj:`X509Type` can be retrieved and modified with + :py:obj:`X509Type.get_serial_number` and :py:obj:`X509Type.set_serial_number`. """ certificate = X509() self.assertRaises(TypeError, certificate.set_serial_number) @@ -1152,7 +1292,7 @@ WpOdIpB8KksUTCzV591Nr1wd def _setBoundTest(self, which): """ - L{X509Type.set_notBefore} takes a string in the format of an ASN1 + :py:obj:`X509Type.set_notBefore` takes a string in the format of an ASN1 GENERALIZEDTIME and sets the beginning of the certificate's validity period to it. """ @@ -1191,7 +1331,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_set_notBefore(self): """ - L{X509Type.set_notBefore} takes a string in the format of an ASN1 + :py:obj:`X509Type.set_notBefore` takes a string in the format of an ASN1 GENERALIZEDTIME and sets the beginning of the certificate's validity period to it. """ @@ -1200,7 +1340,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_set_notAfter(self): """ - L{X509Type.set_notAfter} takes a string in the format of an ASN1 + :py:obj:`X509Type.set_notAfter` takes a string in the format of an ASN1 GENERALIZEDTIME and sets the end of the certificate's validity period to it. """ @@ -1209,7 +1349,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_get_notBefore(self): """ - L{X509Type.get_notBefore} returns a string in the format of an ASN1 + :py:obj:`X509Type.get_notBefore` returns a string in the format of an ASN1 GENERALIZEDTIME even for certificates which store it as UTCTIME internally. """ @@ -1219,7 +1359,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_get_notAfter(self): """ - L{X509Type.get_notAfter} returns a string in the format of an ASN1 + :py:obj:`X509Type.get_notAfter` returns a string in the format of an ASN1 GENERALIZEDTIME even for certificates which store it as UTCTIME internally. """ @@ -1229,8 +1369,8 @@ WpOdIpB8KksUTCzV591Nr1wd def test_gmtime_adj_notBefore_wrong_args(self): """ - L{X509Type.gmtime_adj_notBefore} raises L{TypeError} if called with the - wrong number of arguments or a non-C{int} argument. + :py:obj:`X509Type.gmtime_adj_notBefore` raises :py:obj:`TypeError` if called with the + wrong number of arguments or a non-:py:obj:`int` argument. """ cert = X509() self.assertRaises(TypeError, cert.gmtime_adj_notBefore) @@ -1240,7 +1380,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_gmtime_adj_notBefore(self): """ - L{X509Type.gmtime_adj_notBefore} changes the not-before timestamp to be + :py:obj:`X509Type.gmtime_adj_notBefore` changes the not-before timestamp to be the current time plus the number of seconds passed in. """ cert = load_certificate(FILETYPE_PEM, self.pemData) @@ -1251,8 +1391,8 @@ WpOdIpB8KksUTCzV591Nr1wd def test_gmtime_adj_notAfter_wrong_args(self): """ - L{X509Type.gmtime_adj_notAfter} raises L{TypeError} if called with the - wrong number of arguments or a non-C{int} argument. + :py:obj:`X509Type.gmtime_adj_notAfter` raises :py:obj:`TypeError` if called with the + wrong number of arguments or a non-:py:obj:`int` argument. """ cert = X509() self.assertRaises(TypeError, cert.gmtime_adj_notAfter) @@ -1262,7 +1402,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_gmtime_adj_notAfter(self): """ - L{X509Type.gmtime_adj_notAfter} changes the not-after timestamp to be + :py:obj:`X509Type.gmtime_adj_notAfter` changes the not-after timestamp to be the current time plus the number of seconds passed in. """ cert = load_certificate(FILETYPE_PEM, self.pemData) @@ -1273,7 +1413,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_has_expired_wrong_args(self): """ - L{X509Type.has_expired} raises L{TypeError} if called with any + :py:obj:`X509Type.has_expired` raises :py:obj:`TypeError` if called with any arguments. """ cert = X509() @@ -1282,7 +1422,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_has_expired(self): """ - L{X509Type.has_expired} returns C{True} if the certificate's not-after + :py:obj:`X509Type.has_expired` returns :py:obj:`True` if the certificate's not-after time is in the past. """ cert = X509() @@ -1292,7 +1432,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_has_not_expired(self): """ - L{X509Type.has_expired} returns C{False} if the certificate's not-after + :py:obj:`X509Type.has_expired` returns :py:obj:`False` if the certificate's not-after time is in the future. """ cert = X509() @@ -1302,12 +1442,15 @@ WpOdIpB8KksUTCzV591Nr1wd def test_digest(self): """ - L{X509.digest} returns a string giving ":"-separated hex-encoded words + :py:obj:`X509.digest` returns a string giving ":"-separated hex-encoded words of the digest of the certificate. """ cert = X509() self.assertEqual( - cert.digest("md5"), + # This is MD5 instead of GOOD_DIGEST because the digest algorithm + # actually matters to the assertion (ie, another arbitrary, good + # digest will not product the same digest). + cert.digest("MD5"), b("A8:EB:07:F8:53:25:0A:F2:56:05:C5:A5:C4:C4:C7:15")) @@ -1327,7 +1470,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_extension_count(self): """ - L{X509.get_extension_count} returns the number of extensions that are + :py:obj:`X509.get_extension_count` returns the number of extensions that are present in the certificate. """ pkey = load_privatekey(FILETYPE_PEM, client_key_pem) @@ -1351,7 +1494,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_get_extension(self): """ - L{X509.get_extension} takes an integer and returns an L{X509Extension} + :py:obj:`X509.get_extension` takes an integer and returns an :py:obj:`X509Extension` corresponding to the extension at that index. """ pkey = load_privatekey(FILETYPE_PEM, client_key_pem) @@ -1382,18 +1525,36 @@ WpOdIpB8KksUTCzV591Nr1wd self.assertRaises(TypeError, cert.get_extension, "hello") + def test_nullbyte_subjectAltName(self): + """ + The fields of a `subjectAltName` extension on an X509 may contain NUL + bytes and this value is reflected in the string representation of the + extension object. + """ + cert = load_certificate(FILETYPE_PEM, nulbyteSubjectAltNamePEM) + + ext = cert.get_extension(3) + self.assertEqual(ext.get_short_name(), b('subjectAltName')) + self.assertEqual( + b("DNS:altnull.python.org\x00example.com, " + "email:null@python.org\x00user@example.org, " + "URI:http://null.python.org\x00http://example.org, " + "IP Address:192.0.2.1, IP Address:2001:DB8:0:0:0:0:0:1\n"), + b(str(ext))) + + def test_invalid_digest_algorithm(self): """ - L{X509.digest} raises L{ValueError} if called with an unrecognized hash + :py:obj:`X509.digest` raises :py:obj:`ValueError` if called with an unrecognized hash algorithm. """ cert = X509() - self.assertRaises(ValueError, cert.digest, "monkeys") + self.assertRaises(ValueError, cert.digest, BAD_DIGEST) def test_get_subject_wrong_args(self): """ - L{X509.get_subject} raises L{TypeError} if called with any arguments. + :py:obj:`X509.get_subject` raises :py:obj:`TypeError` if called with any arguments. """ cert = X509() self.assertRaises(TypeError, cert.get_subject, None) @@ -1401,7 +1562,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_get_subject(self): """ - L{X509.get_subject} returns an L{X509Name} instance. + :py:obj:`X509.get_subject` returns an :py:obj:`X509Name` instance. """ cert = load_certificate(FILETYPE_PEM, self.pemData) subj = cert.get_subject() @@ -1414,8 +1575,8 @@ WpOdIpB8KksUTCzV591Nr1wd def test_set_subject_wrong_args(self): """ - L{X509.set_subject} raises a L{TypeError} if called with the wrong - number of arguments or an argument not of type L{X509Name}. + :py:obj:`X509.set_subject` raises a :py:obj:`TypeError` if called with the wrong + number of arguments or an argument not of type :py:obj:`X509Name`. """ cert = X509() self.assertRaises(TypeError, cert.set_subject) @@ -1425,7 +1586,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_set_subject(self): """ - L{X509.set_subject} changes the subject of the certificate to the one + :py:obj:`X509.set_subject` changes the subject of the certificate to the one passed in. """ cert = X509() @@ -1440,7 +1601,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_get_issuer_wrong_args(self): """ - L{X509.get_issuer} raises L{TypeError} if called with any arguments. + :py:obj:`X509.get_issuer` raises :py:obj:`TypeError` if called with any arguments. """ cert = X509() self.assertRaises(TypeError, cert.get_issuer, None) @@ -1448,7 +1609,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_get_issuer(self): """ - L{X509.get_issuer} returns an L{X509Name} instance. + :py:obj:`X509.get_issuer` returns an :py:obj:`X509Name` instance. """ cert = load_certificate(FILETYPE_PEM, self.pemData) subj = cert.get_issuer() @@ -1462,8 +1623,8 @@ WpOdIpB8KksUTCzV591Nr1wd def test_set_issuer_wrong_args(self): """ - L{X509.set_issuer} raises a L{TypeError} if called with the wrong - number of arguments or an argument not of type L{X509Name}. + :py:obj:`X509.set_issuer` raises a :py:obj:`TypeError` if called with the wrong + number of arguments or an argument not of type :py:obj:`X509Name`. """ cert = X509() self.assertRaises(TypeError, cert.set_issuer) @@ -1473,7 +1634,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_set_issuer(self): """ - L{X509.set_issuer} changes the issuer of the certificate to the one + :py:obj:`X509.set_issuer` changes the issuer of the certificate to the one passed in. """ cert = X509() @@ -1488,8 +1649,8 @@ WpOdIpB8KksUTCzV591Nr1wd def test_get_pubkey_uninitialized(self): """ - When called on a certificate with no public key, L{X509.get_pubkey} - raises L{OpenSSL.crypto.Error}. + When called on a certificate with no public key, :py:obj:`X509.get_pubkey` + raises :py:obj:`OpenSSL.crypto.Error`. """ cert = X509() self.assertRaises(Error, cert.get_pubkey) @@ -1497,7 +1658,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_subject_name_hash_wrong_args(self): """ - L{X509.subject_name_hash} raises L{TypeError} if called with any + :py:obj:`X509.subject_name_hash` raises :py:obj:`TypeError` if called with any arguments. """ cert = X509() @@ -1506,7 +1667,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_subject_name_hash(self): """ - L{X509.subject_name_hash} returns the hash of the certificate's subject + :py:obj:`X509.subject_name_hash` returns the hash of the certificate's subject name. """ cert = load_certificate(FILETYPE_PEM, self.pemData) @@ -1519,7 +1680,7 @@ WpOdIpB8KksUTCzV591Nr1wd def test_get_signature_algorithm(self): """ - L{X509Type.get_signature_algorithm} returns a string which means + :py:obj:`X509Type.get_signature_algorithm` returns a string which means the algorithm used to sign the certificate. """ cert = load_certificate(FILETYPE_PEM, self.pemData) @@ -1529,12 +1690,12 @@ WpOdIpB8KksUTCzV591Nr1wd def test_get_undefined_signature_algorithm(self): """ - L{X509Type.get_signature_algorithm} raises L{ValueError} if the + :py:obj:`X509Type.get_signature_algorithm` raises :py:obj:`ValueError` if the signature algorithm is undefined or unknown. """ # This certificate has been modified to indicate a bogus OID in the # signature algorithm field so that OpenSSL does not recognize it. - certPEM = """\ + certPEM = b("""\ -----BEGIN CERTIFICATE----- MIIC/zCCAmigAwIBAgIBATAGBgJ8BQUAMHsxCzAJBgNVBAYTAlNHMREwDwYDVQQK EwhNMkNyeXB0bzEUMBIGA1UECxMLTTJDcnlwdG8gQ0ExJDAiBgNVBAMTG00yQ3J5 @@ -1554,21 +1715,62 @@ jEY7xKfpQngV599k1xhl11IMqizDwu0855agrckg2MCTmOI9DZzDD77tAYb+Dk0O PEVk0Mk/V0aIsDE9bolfCi/i/QWZ3N8s5nTWMNyBBBmoSliWCm4jkkRZRD0ejgTN tgI5 -----END CERTIFICATE----- -""" +""") cert = load_certificate(FILETYPE_PEM, certPEM) self.assertRaises(ValueError, cert.get_signature_algorithm) +class X509StoreTests(TestCase): + """ + Test for :py:obj:`OpenSSL.crypto.X509Store`. + """ + def test_type(self): + """ + :py:obj:`X509StoreType` is a type object. + """ + self.assertIdentical(X509Store, X509StoreType) + self.assertConsistentType(X509Store, 'X509Store') + + + def test_add_cert_wrong_args(self): + store = X509Store() + self.assertRaises(TypeError, store.add_cert) + self.assertRaises(TypeError, store.add_cert, object()) + self.assertRaises(TypeError, store.add_cert, X509(), object()) + + + def test_add_cert(self): + """ + :py:obj:`X509Store.add_cert` adds a :py:obj:`X509` instance to the + certificate store. + """ + cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM) + store = X509Store() + store.add_cert(cert) + + + def test_add_cert_rejects_duplicate(self): + """ + :py:obj:`X509Store.add_cert` raises :py:obj:`OpenSSL.crypto.Error` if an + attempt is made to add the same certificate to the store more than once. + """ + cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM) + store = X509Store() + store.add_cert(cert) + self.assertRaises(Error, store.add_cert, cert) + + + class PKCS12Tests(TestCase): """ - Test for L{OpenSSL.crypto.PKCS12} and L{OpenSSL.crypto.load_pkcs12}. + Test for :py:obj:`OpenSSL.crypto.PKCS12` and :py:obj:`OpenSSL.crypto.load_pkcs12`. """ pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM def test_type(self): """ - L{PKCS12Type} is a type object. + :py:obj:`PKCS12Type` is a type object. """ self.assertIdentical(PKCS12, PKCS12Type) self.assertConsistentType(PKCS12, 'PKCS12') @@ -1576,7 +1778,7 @@ class PKCS12Tests(TestCase): def test_empty_construction(self): """ - L{PKCS12} returns a new instance of L{PKCS12} with no certificate, + :py:obj:`PKCS12` returns a new instance of :py:obj:`PKCS12` with no certificate, private key, CA certificates, or friendly name. """ p12 = PKCS12() @@ -1588,8 +1790,8 @@ class PKCS12Tests(TestCase): def test_type_errors(self): """ - The L{PKCS12} setter functions (C{set_certificate}, C{set_privatekey}, - C{set_ca_certificates}, and C{set_friendlyname}) raise L{TypeError} + The :py:obj:`PKCS12` setter functions (:py:obj:`set_certificate`, :py:obj:`set_privatekey`, + :py:obj:`set_ca_certificates`, and :py:obj:`set_friendlyname`) raise :py:obj:`TypeError` when passed objects of types other than those expected. """ p12 = PKCS12() @@ -1609,10 +1811,10 @@ class PKCS12Tests(TestCase): def test_key_only(self): """ - A L{PKCS12} with only a private key can be exported using - L{PKCS12.export} and loaded again using L{load_pkcs12}. + A :py:obj:`PKCS12` with only a private key can be exported using + :py:obj:`PKCS12.export` and loaded again using :py:obj:`load_pkcs12`. """ - passwd = 'blah' + passwd = b"blah" p12 = PKCS12() pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) p12.set_privatekey(pkey) @@ -1636,10 +1838,10 @@ class PKCS12Tests(TestCase): def test_cert_only(self): """ - A L{PKCS12} with only a certificate can be exported using - L{PKCS12.export} and loaded again using L{load_pkcs12}. + A :py:obj:`PKCS12` with only a certificate can be exported using + :py:obj:`PKCS12.export` and loaded again using :py:obj:`load_pkcs12`. """ - passwd = 'blah' + passwd = b"blah" p12 = PKCS12() cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM) p12.set_certificate(cert) @@ -1690,7 +1892,7 @@ class PKCS12Tests(TestCase): return p12 - def check_recovery(self, p12_str, key=None, cert=None, ca=None, passwd='', + def check_recovery(self, p12_str, key=None, cert=None, ca=None, passwd=b"", extra=()): """ Use openssl program to confirm three components are recoverable from a @@ -1698,30 +1900,30 @@ class PKCS12Tests(TestCase): """ if key: recovered_key = _runopenssl( - p12_str, "pkcs12", '-nocerts', '-nodes', '-passin', - 'pass:' + passwd, *extra) + p12_str, b"pkcs12", b"-nocerts", b"-nodes", b"-passin", + b"pass:" + passwd, *extra) self.assertEqual(recovered_key[-len(key):], key) if cert: recovered_cert = _runopenssl( - p12_str, "pkcs12", '-clcerts', '-nodes', '-passin', - 'pass:' + passwd, '-nokeys', *extra) + p12_str, b"pkcs12", b"-clcerts", b"-nodes", b"-passin", + b"pass:" + passwd, b"-nokeys", *extra) self.assertEqual(recovered_cert[-len(cert):], cert) if ca: recovered_cert = _runopenssl( - p12_str, "pkcs12", '-cacerts', '-nodes', '-passin', - 'pass:' + passwd, '-nokeys', *extra) + p12_str, b"pkcs12", b"-cacerts", b"-nodes", b"-passin", + b"pass:" + passwd, b"-nokeys", *extra) self.assertEqual(recovered_cert[-len(ca):], ca) def test_load_pkcs12(self): """ A PKCS12 string generated using the openssl command line can be loaded - with L{load_pkcs12} and its components extracted and examined. + with :py:obj:`load_pkcs12` and its components extracted and examined. """ - passwd = 'whatever' + passwd = b"whatever" pem = client_key_pem + client_cert_pem p12_str = _runopenssl( - pem, "pkcs12", '-export', '-clcerts', '-passout', 'pass:' + passwd) + pem, b"pkcs12", b"-export", b"-clcerts", b"-passout", b"pass:" + passwd) p12 = load_pkcs12(p12_str, passwd) # verify self.assertTrue(isinstance(p12, PKCS12)) @@ -1734,20 +1936,20 @@ class PKCS12Tests(TestCase): def test_load_pkcs12_garbage(self): """ - L{load_pkcs12} raises L{OpenSSL.crypto.Error} when passed a string + :py:obj:`load_pkcs12` raises :py:obj:`OpenSSL.crypto.Error` when passed a string which is not a PKCS12 dump. """ passwd = 'whatever' - e = self.assertRaises(Error, load_pkcs12, 'fruit loops', passwd) + e = self.assertRaises(Error, load_pkcs12, b'fruit loops', passwd) self.assertEqual( e.args[0][0][0], 'asn1 encoding routines') self.assertEqual( len(e.args[0][0]), 3) def test_replace(self): """ - L{PKCS12.set_certificate} replaces the certificate in a PKCS12 cluster. - L{PKCS12.set_privatekey} replaces the private key. - L{PKCS12.set_ca_certificates} replaces the CA certificates. + :py:obj:`PKCS12.set_certificate` replaces the certificate in a PKCS12 cluster. + :py:obj:`PKCS12.set_privatekey` replaces the private key. + :py:obj:`PKCS12.set_ca_certificates` replaces the CA certificates. """ p12 = self.gen_pkcs12(client_cert_pem, client_key_pem, root_cert_pem) p12.set_certificate(load_certificate(FILETYPE_PEM, server_cert_pem)) @@ -1765,11 +1967,11 @@ class PKCS12Tests(TestCase): def test_friendly_name(self): """ - The I{friendlyName} of a PKCS12 can be set and retrieved via - L{PKCS12.get_friendlyname} and L{PKCS12_set_friendlyname}, and a - L{PKCS12} with a friendly name set can be dumped with L{PKCS12.export}. + The *friendlyName* of a PKCS12 can be set and retrieved via + :py:obj:`PKCS12.get_friendlyname` and :py:obj:`PKCS12_set_friendlyname`, and a + :py:obj:`PKCS12` with a friendly name set can be dumped with :py:obj:`PKCS12.export`. """ - passwd = 'Dogmeat[]{}!@#$%^&*()~`?/.,<>-_+=";:' + passwd = b'Dogmeat[]{}!@#$%^&*()~`?/.,<>-_+=";:' p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem) for friendly_name in [b('Serverlicious'), None, b('###')]: p12.set_friendlyname(friendly_name) @@ -1793,7 +1995,7 @@ class PKCS12Tests(TestCase): export. """ p12 = self.gen_pkcs12(client_cert_pem, client_key_pem, root_cert_pem) - passwd = '' + passwd = b"" dumped_p12_empty = p12.export(iter=2, maciter=0, passphrase=passwd) dumped_p12_none = p12.export(iter=3, maciter=2, passphrase=None) dumped_p12_nopw = p12.export(iter=9, maciter=4) @@ -1805,7 +2007,7 @@ class PKCS12Tests(TestCase): def test_removing_ca_cert(self): """ - Passing C{None} to L{PKCS12.set_ca_certificates} removes all CA + Passing :py:obj:`None` to :py:obj:`PKCS12.set_ca_certificates` removes all CA certificates. """ p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem) @@ -1815,22 +2017,22 @@ class PKCS12Tests(TestCase): def test_export_without_mac(self): """ - Exporting a PKCS12 with a C{maciter} of C{-1} excludes the MAC + Exporting a PKCS12 with a :py:obj:`maciter` of ``-1`` excludes the MAC entirely. """ - passwd = 'Lake Michigan' + passwd = b"Lake Michigan" p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem) dumped_p12 = p12.export(maciter=-1, passphrase=passwd, iter=2) self.check_recovery( dumped_p12, key=server_key_pem, cert=server_cert_pem, - passwd=passwd, extra=('-nomacver',)) + passwd=passwd, extra=(b"-nomacver",)) def test_load_without_mac(self): """ Loading a PKCS12 without a MAC does something other than crash. """ - passwd = 'Lake Michigan' + passwd = b"Lake Michigan" p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem) dumped_p12 = p12.export(maciter=-1, passphrase=passwd, iter=2) try: @@ -1853,27 +2055,27 @@ class PKCS12Tests(TestCase): """ passwd = 'Hobie 18' p12 = self.gen_pkcs12(server_cert_pem, server_key_pem) - p12.set_ca_certificates([]) - self.assertEqual((), p12.get_ca_certificates()) - dumped_p12 = p12.export(passphrase=passwd, iter=3) - self.check_recovery( - dumped_p12, key=server_key_pem, cert=server_cert_pem, - passwd=passwd) + # p12.set_ca_certificates([]) + # self.assertEqual((), p12.get_ca_certificates()) + # dumped_p12 = p12.export(passphrase=passwd, iter=3) + # self.check_recovery( + # dumped_p12, key=server_key_pem, cert=server_cert_pem, + # passwd=passwd) def test_export_without_args(self): """ - All the arguments to L{PKCS12.export} are optional. + All the arguments to :py:obj:`PKCS12.export` are optional. """ p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem) dumped_p12 = p12.export() # no args self.check_recovery( - dumped_p12, key=server_key_pem, cert=server_cert_pem, passwd='') + dumped_p12, key=server_key_pem, cert=server_cert_pem, passwd=b"") def test_key_cert_mismatch(self): """ - L{PKCS12.export} raises an exception when a key and certificate + :py:obj:`PKCS12.export` raises an exception when a key and certificate mismatch. """ p12 = self.gen_pkcs12(server_cert_pem, client_key_pem, root_cert_pem) @@ -1882,39 +2084,39 @@ class PKCS12Tests(TestCase): # These quoting functions taken directly from Twisted's twisted.python.win32. -_cmdLineQuoteRe = re.compile(r'(\\*)"') -_cmdLineQuoteRe2 = re.compile(r'(\\+)\Z') +_cmdLineQuoteRe = re.compile(br'(\\*)"') +_cmdLineQuoteRe2 = re.compile(br'(\\+)\Z') def cmdLineQuote(s): """ Internal method for quoting a single command-line argument. - @type: C{str} - @param s: A single unquoted string to quote for something that is expecting - cmd.exe-style quoting + See http://www.perlmonks.org/?node_id=764004 - @rtype: C{str} - @return: A cmd.exe-style quoted string + :type: :py:obj:`str` + :param s: A single unquoted string to quote for something that is expecting + cmd.exe-style quoting - @see: U{http://www.perlmonks.org/?node_id=764004} + :rtype: :py:obj:`str` + :return: A cmd.exe-style quoted string """ - s = _cmdLineQuoteRe2.sub(r"\1\1", _cmdLineQuoteRe.sub(r'\1\1\\"', s)) - return '"%s"' % s + s = _cmdLineQuoteRe2.sub(br"\1\1", _cmdLineQuoteRe.sub(br'\1\1\\"', s)) + return b'"' + s + b'"' def quoteArguments(arguments): """ Quote an iterable of command-line arguments for passing to CreateProcess or - a similar API. This allows the list passed to C{reactor.spawnProcess} to - match the child process's C{sys.argv} properly. + a similar API. This allows the list passed to :py:obj:`reactor.spawnProcess` to + match the child process's :py:obj:`sys.argv` properly. - @type arguments: C{iterable} of C{str} - @param arguments: An iterable of unquoted arguments to quote + :type arguments: :py:obj:`iterable` of :py:obj:`str` + :param arguments: An iterable of unquoted arguments to quote - @rtype: C{str} - @return: A space-delimited string containing quoted versions of L{arguments} + :rtype: :py:obj:`str` + :return: A space-delimited string containing quoted versions of :py:obj:`arguments` """ - return ' '.join(map(cmdLineQuote, arguments)) + return b' '.join(map(cmdLineQuote, arguments)) @@ -1924,33 +2126,37 @@ def _runopenssl(pem, *args): the given PEM to its stdin. Not safe for quotes. """ if os.name == 'posix': - command = "openssl " + " ".join([ - "'%s'" % (arg.replace("'", "'\\''"),) for arg in args]) + command = b"openssl " + b" ".join([ + (b"'" + arg.replace(b"'", b"'\\''") + b"'") + for arg in args]) else: - command = "openssl " + quoteArguments(args) - proc = Popen(command, shell=True, stdin=PIPE, stdout=PIPE) + command = b"openssl " + quoteArguments(args) + proc = Popen(native(command), shell=True, stdin=PIPE, stdout=PIPE) proc.stdin.write(pem) proc.stdin.close() - return proc.stdout.read() + output = proc.stdout.read() + proc.stdout.close() + proc.wait() + return output class FunctionTests(TestCase): """ - Tests for free-functions in the L{OpenSSL.crypto} module. + Tests for free-functions in the :py:obj:`OpenSSL.crypto` module. """ def test_load_privatekey_invalid_format(self): """ - L{load_privatekey} raises L{ValueError} if passed an unknown filetype. + :py:obj:`load_privatekey` raises :py:obj:`ValueError` if passed an unknown filetype. """ self.assertRaises(ValueError, load_privatekey, 100, root_key_pem) def test_load_privatekey_invalid_passphrase_type(self): """ - L{load_privatekey} raises L{TypeError} if passed a passphrase that is - neither a c{str} nor a callable. + :py:obj:`load_privatekey` raises :py:obj:`TypeError` if passed a passphrase that is + neither a :py:obj:`str` nor a callable. """ self.assertRaises( TypeError, @@ -1960,7 +2166,7 @@ class FunctionTests(TestCase): def test_load_privatekey_wrong_args(self): """ - L{load_privatekey} raises L{TypeError} if called with the wrong number + :py:obj:`load_privatekey` raises :py:obj:`TypeError` if called with the wrong number of arguments. """ self.assertRaises(TypeError, load_privatekey) @@ -1968,7 +2174,7 @@ class FunctionTests(TestCase): def test_load_privatekey_wrongPassphrase(self): """ - L{load_privatekey} raises L{OpenSSL.crypto.Error} when it is passed an + :py:obj:`load_privatekey` raises :py:obj:`OpenSSL.crypto.Error` when it is passed an encrypted PEM and an incorrect passphrase. """ self.assertRaises( @@ -1976,9 +2182,21 @@ class FunctionTests(TestCase): load_privatekey, FILETYPE_PEM, encryptedPrivateKeyPEM, b("quack")) + def test_load_privatekey_passphraseWrongType(self): + """ + :py:obj:`load_privatekey` raises :py:obj:`ValueError` when it is passed a passphrase + with a private key encoded in a format, that doesn't support + encryption. + """ + key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) + blob = dump_privatekey(FILETYPE_ASN1, key) + self.assertRaises(ValueError, + load_privatekey, FILETYPE_ASN1, blob, "secret") + + def test_load_privatekey_passphrase(self): """ - L{load_privatekey} can create a L{PKey} object from an encrypted PEM + :py:obj:`load_privatekey` can create a :py:obj:`PKey` object from an encrypted PEM string if given the passphrase. """ key = load_privatekey( @@ -1987,16 +2205,28 @@ class FunctionTests(TestCase): self.assertTrue(isinstance(key, PKeyType)) + def test_load_privatekey_passphrase_exception(self): + """ + If the passphrase callback raises an exception, that exception is raised + by :py:obj:`load_privatekey`. + """ + def cb(ignored): + raise ArithmeticError + + self.assertRaises(ArithmeticError, + load_privatekey, FILETYPE_PEM, encryptedPrivateKeyPEM, cb) + + 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. + :py:obj:`load_privatekey` raises :py:obj:`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" + return b("quack") self.assertRaises( Error, load_privatekey, FILETYPE_PEM, encryptedPrivateKeyPEM, cb) @@ -2005,7 +2235,7 @@ class FunctionTests(TestCase): def test_load_privatekey_passphraseCallback(self): """ - L{load_privatekey} can create a L{PKey} object from an encrypted PEM + :py:obj:`load_privatekey` can create a :py:obj:`PKey` object from an encrypted PEM string if given a passphrase callback which returns the correct password. """ @@ -2018,57 +2248,55 @@ class FunctionTests(TestCase): self.assertEqual(called, [False]) - def test_load_privatekey_passphrase_exception(self): + def test_load_privatekey_passphrase_wrong_return_type(self): """ - An exception raised by the passphrase callback passed to - L{load_privatekey} causes L{OpenSSL.crypto.Error} to be raised. - - This isn't as nice as just letting the exception pass through. The - behavior might be changed to that eventually. + :py:obj:`load_privatekey` raises :py:obj:`ValueError` if the passphrase + callback returns something other than a byte string. """ - def broken(ignored): - raise RuntimeError("This is not working.") self.assertRaises( - Error, + ValueError, load_privatekey, - FILETYPE_PEM, encryptedPrivateKeyPEM, broken) + FILETYPE_PEM, encryptedPrivateKeyPEM, lambda *args: 3) def test_dump_privatekey_wrong_args(self): """ - L{dump_privatekey} raises L{TypeError} if called with the wrong number + :py:obj:`dump_privatekey` raises :py:obj:`TypeError` if called with the wrong number of arguments. """ self.assertRaises(TypeError, dump_privatekey) + # If cipher name is given, password is required. + self.assertRaises( + TypeError, dump_privatekey, FILETYPE_PEM, PKey(), GOOD_CIPHER) def test_dump_privatekey_unknown_cipher(self): """ - L{dump_privatekey} raises L{ValueError} if called with an unrecognized + :py:obj:`dump_privatekey` raises :py:obj:`ValueError` if called with an unrecognized cipher name. """ key = PKey() key.generate_key(TYPE_RSA, 512) self.assertRaises( ValueError, dump_privatekey, - FILETYPE_PEM, key, "zippers", "passphrase") + FILETYPE_PEM, key, BAD_CIPHER, "passphrase") def test_dump_privatekey_invalid_passphrase_type(self): """ - L{dump_privatekey} raises L{TypeError} if called with a passphrase which - is neither a C{str} nor a callable. + :py:obj:`dump_privatekey` raises :py:obj:`TypeError` if called with a passphrase which + is neither a :py:obj:`str` nor a callable. """ key = PKey() key.generate_key(TYPE_RSA, 512) self.assertRaises( TypeError, - dump_privatekey, FILETYPE_PEM, key, "blowfish", object()) + dump_privatekey, FILETYPE_PEM, key, GOOD_CIPHER, object()) def test_dump_privatekey_invalid_filetype(self): """ - L{dump_privatekey} raises L{ValueError} if called with an unrecognized + :py:obj:`dump_privatekey` raises :py:obj:`ValueError` if called with an unrecognized filetype. """ key = PKey() @@ -2076,81 +2304,122 @@ class FunctionTests(TestCase): self.assertRaises(ValueError, dump_privatekey, 100, key) + def test_load_privatekey_passphraseCallbackLength(self): + """ + :py:obj:`crypto.load_privatekey` should raise an error when the passphrase + provided by the callback is too long, not silently truncate it. + """ + def cb(ignored): + return "a" * 1025 + + self.assertRaises(ValueError, + load_privatekey, FILETYPE_PEM, encryptedPrivateKeyPEM, cb) + + def test_dump_privatekey_passphrase(self): """ - L{dump_privatekey} writes an encrypted PEM when given a passphrase. + :py:obj:`dump_privatekey` writes an encrypted PEM when given a passphrase. """ passphrase = b("foo") key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) - pem = dump_privatekey(FILETYPE_PEM, key, "blowfish", passphrase) - self.assertTrue(isinstance(pem, bytes)) + pem = dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, passphrase) + self.assertTrue(isinstance(pem, binary_type)) 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_passphraseWrongType(self): + """ + :py:obj:`dump_privatekey` raises :py:obj:`ValueError` when it is passed a passphrase + with a private key encoded in a format, that doesn't support + encryption. + """ + key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) + self.assertRaises(ValueError, + dump_privatekey, FILETYPE_ASN1, key, GOOD_CIPHER, "secret") + + def test_dump_certificate(self): """ - L{dump_certificate} writes PEM, DER, and text. + :py:obj:`dump_certificate` writes PEM, DER, and text. """ pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM cert = load_certificate(FILETYPE_PEM, pemData) dumped_pem = dump_certificate(FILETYPE_PEM, cert) self.assertEqual(dumped_pem, cleartextCertificatePEM) dumped_der = dump_certificate(FILETYPE_ASN1, cert) - good_der = _runopenssl(dumped_pem, "x509", "-outform", "DER") + good_der = _runopenssl(dumped_pem, b"x509", b"-outform", b"DER") self.assertEqual(dumped_der, good_der) cert2 = load_certificate(FILETYPE_ASN1, dumped_der) dumped_pem2 = dump_certificate(FILETYPE_PEM, cert2) self.assertEqual(dumped_pem2, cleartextCertificatePEM) dumped_text = dump_certificate(FILETYPE_TEXT, cert) - good_text = _runopenssl(dumped_pem, "x509", "-noout", "-text") + good_text = _runopenssl(dumped_pem, b"x509", b"-noout", b"-text") self.assertEqual(dumped_text, good_text) - def test_dump_privatekey(self): + def test_dump_privatekey_pem(self): """ - L{dump_privatekey} writes a PEM, DER, and text. + :py:obj:`dump_privatekey` writes a PEM """ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) self.assertTrue(key.check()) dumped_pem = dump_privatekey(FILETYPE_PEM, key) self.assertEqual(dumped_pem, cleartextPrivateKeyPEM) + + + def test_dump_privatekey_asn1(self): + """ + :py:obj:`dump_privatekey` writes a DER + """ + key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) + dumped_pem = dump_privatekey(FILETYPE_PEM, key) + dumped_der = dump_privatekey(FILETYPE_ASN1, key) # XXX This OpenSSL call writes "writing RSA key" to standard out. Sad. - good_der = _runopenssl(dumped_pem, "rsa", "-outform", "DER") + good_der = _runopenssl(dumped_pem, b"rsa", b"-outform", b"DER") self.assertEqual(dumped_der, good_der) key2 = load_privatekey(FILETYPE_ASN1, dumped_der) dumped_pem2 = dump_privatekey(FILETYPE_PEM, key2) self.assertEqual(dumped_pem2, cleartextPrivateKeyPEM) + + + def test_dump_privatekey_text(self): + """ + :py:obj:`dump_privatekey` writes a text + """ + key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) + dumped_pem = dump_privatekey(FILETYPE_PEM, key) + dumped_text = dump_privatekey(FILETYPE_TEXT, key) - good_text = _runopenssl(dumped_pem, "rsa", "-noout", "-text") + good_text = _runopenssl(dumped_pem, b"rsa", b"-noout", b"-text") self.assertEqual(dumped_text, good_text) def test_dump_certificate_request(self): """ - L{dump_certificate_request} writes a PEM, DER, and text. + :py:obj:`dump_certificate_request` writes a PEM, DER, and text. """ req = load_certificate_request(FILETYPE_PEM, cleartextCertificateRequestPEM) dumped_pem = dump_certificate_request(FILETYPE_PEM, req) self.assertEqual(dumped_pem, cleartextCertificateRequestPEM) dumped_der = dump_certificate_request(FILETYPE_ASN1, req) - good_der = _runopenssl(dumped_pem, "req", "-outform", "DER") + good_der = _runopenssl(dumped_pem, b"req", b"-outform", b"DER") self.assertEqual(dumped_der, good_der) req2 = load_certificate_request(FILETYPE_ASN1, dumped_der) dumped_pem2 = dump_certificate_request(FILETYPE_PEM, req2) self.assertEqual(dumped_pem2, cleartextCertificateRequestPEM) dumped_text = dump_certificate_request(FILETYPE_TEXT, req) - good_text = _runopenssl(dumped_pem, "req", "-noout", "-text") + good_text = _runopenssl(dumped_pem, b"req", b"-noout", b"-text") self.assertEqual(dumped_text, good_text) self.assertRaises(ValueError, dump_certificate_request, 100, req) def test_dump_privatekey_passphraseCallback(self): """ - L{dump_privatekey} writes an encrypted PEM when given a callback which + :py:obj:`dump_privatekey` writes an encrypted PEM when given a callback which returns the correct passphrase. """ passphrase = b("foo") @@ -2159,8 +2428,8 @@ class FunctionTests(TestCase): called.append(writing) return passphrase key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) - pem = dump_privatekey(FILETYPE_PEM, key, "blowfish", cb) - self.assertTrue(isinstance(pem, bytes)) + pem = dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, cb) + self.assertTrue(isinstance(pem, binary_type)) self.assertEqual(called, [True]) loadedKey = load_privatekey(FILETYPE_PEM, pem, passphrase) self.assertTrue(isinstance(loadedKey, PKeyType)) @@ -2168,23 +2437,71 @@ class FunctionTests(TestCase): self.assertEqual(loadedKey.bits(), key.bits()) + def test_dump_privatekey_passphrase_exception(self): + """ + :py:obj:`dump_privatekey` should not overwrite the exception raised + by the passphrase callback. + """ + def cb(ignored): + raise ArithmeticError + + key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) + self.assertRaises(ArithmeticError, + dump_privatekey, FILETYPE_PEM, key, GOOD_CIPHER, cb) + + + def test_dump_privatekey_passphraseCallbackLength(self): + """ + :py:obj:`crypto.dump_privatekey` should raise an error when the passphrase + provided by the callback is too long, not silently truncate it. + """ + def cb(ignored): + return "a" * 1025 + + key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) + self.assertRaises(ValueError, + dump_privatekey, FILETYPE_PEM, key, GOOD_CIPHER, cb) + + def test_load_pkcs7_data(self): """ - L{load_pkcs7_data} accepts a PKCS#7 string and returns an instance of - L{PKCS7Type}. + :py:obj:`load_pkcs7_data` accepts a PKCS#7 string and returns an instance of + :py:obj:`PKCS7Type`. """ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data) self.assertTrue(isinstance(pkcs7, PKCS7Type)) + def test_load_pkcs7_data_invalid(self): + """ + If the data passed to :py:obj:`load_pkcs7_data` is invalid, + :py:obj:`Error` is raised. + """ + self.assertRaises(Error, load_pkcs7_data, FILETYPE_PEM, b"foo") + + + +class LoadCertificateTests(TestCase): + """ + Tests for :py:obj:`load_certificate_request`. + """ + def test_badFileType(self): + """ + If the file type passed to :py:obj:`load_certificate_request` is + neither :py:obj:`FILETYPE_PEM` nor :py:obj:`FILETYPE_ASN1` then + :py:class:`ValueError` is raised. + """ + self.assertRaises(ValueError, load_certificate_request, object(), b"") + + class PKCS7Tests(TestCase): """ - Tests for L{PKCS7Type}. + Tests for :py:obj:`PKCS7Type`. """ def test_type(self): """ - L{PKCS7Type} is a type object. + :py:obj:`PKCS7Type` is a type object. """ self.assertTrue(isinstance(PKCS7Type, type)) self.assertEqual(PKCS7Type.__name__, 'PKCS7') @@ -2197,7 +2514,7 @@ class PKCS7Tests(TestCase): def test_type_is_signed_wrong_args(self): """ - L{PKCS7Type.type_is_signed} raises L{TypeError} if called with any + :py:obj:`PKCS7Type.type_is_signed` raises :py:obj:`TypeError` if called with any arguments. """ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data) @@ -2206,8 +2523,8 @@ class PKCS7Tests(TestCase): def test_type_is_signed(self): """ - L{PKCS7Type.type_is_signed} returns C{True} if the PKCS7 object is of - the type I{signed}. + :py:obj:`PKCS7Type.type_is_signed` returns :py:obj:`True` if the PKCS7 object is of + the type *signed*. """ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data) self.assertTrue(pkcs7.type_is_signed()) @@ -2215,7 +2532,7 @@ class PKCS7Tests(TestCase): def test_type_is_enveloped_wrong_args(self): """ - L{PKCS7Type.type_is_enveloped} raises L{TypeError} if called with any + :py:obj:`PKCS7Type.type_is_enveloped` raises :py:obj:`TypeError` if called with any arguments. """ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data) @@ -2224,8 +2541,8 @@ class PKCS7Tests(TestCase): def test_type_is_enveloped(self): """ - L{PKCS7Type.type_is_enveloped} returns C{False} if the PKCS7 object is - not of the type I{enveloped}. + :py:obj:`PKCS7Type.type_is_enveloped` returns :py:obj:`False` if the PKCS7 object is + not of the type *enveloped*. """ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data) self.assertFalse(pkcs7.type_is_enveloped()) @@ -2233,7 +2550,7 @@ class PKCS7Tests(TestCase): def test_type_is_signedAndEnveloped_wrong_args(self): """ - L{PKCS7Type.type_is_signedAndEnveloped} raises L{TypeError} if called + :py:obj:`PKCS7Type.type_is_signedAndEnveloped` raises :py:obj:`TypeError` if called with any arguments. """ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data) @@ -2242,8 +2559,8 @@ class PKCS7Tests(TestCase): def test_type_is_signedAndEnveloped(self): """ - L{PKCS7Type.type_is_signedAndEnveloped} returns C{False} if the PKCS7 - object is not of the type I{signed and enveloped}. + :py:obj:`PKCS7Type.type_is_signedAndEnveloped` returns :py:obj:`False` if the PKCS7 + object is not of the type *signed and enveloped*. """ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data) self.assertFalse(pkcs7.type_is_signedAndEnveloped()) @@ -2251,7 +2568,7 @@ class PKCS7Tests(TestCase): def test_type_is_data(self): """ - L{PKCS7Type.type_is_data} returns C{False} if the PKCS7 object is not of + :py:obj:`PKCS7Type.type_is_data` returns :py:obj:`False` if the PKCS7 object is not of the type data. """ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data) @@ -2260,7 +2577,7 @@ class PKCS7Tests(TestCase): def test_type_is_data_wrong_args(self): """ - L{PKCS7Type.type_is_data} raises L{TypeError} if called with any + :py:obj:`PKCS7Type.type_is_data` raises :py:obj:`TypeError` if called with any arguments. """ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data) @@ -2269,7 +2586,7 @@ class PKCS7Tests(TestCase): def test_get_type_name_wrong_args(self): """ - L{PKCS7Type.get_type_name} raises L{TypeError} if called with any + :py:obj:`PKCS7Type.get_type_name` raises :py:obj:`TypeError` if called with any arguments. """ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data) @@ -2278,7 +2595,7 @@ class PKCS7Tests(TestCase): def test_get_type_name(self): """ - L{PKCS7Type.get_type_name} returns a C{str} giving the type name. + :py:obj:`PKCS7Type.get_type_name` returns a :py:obj:`str` giving the type name. """ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data) self.assertEquals(pkcs7.get_type_name(), b('pkcs7-signedData')) @@ -2287,7 +2604,7 @@ class PKCS7Tests(TestCase): def test_attribute(self): """ If an attribute other than one of the methods tested here is accessed on - an instance of L{PKCS7Type}, L{AttributeError} is raised. + an instance of :py:obj:`PKCS7Type`, :py:obj:`AttributeError` is raised. """ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data) self.assertRaises(AttributeError, getattr, pkcs7, "foo") @@ -2296,18 +2613,18 @@ class PKCS7Tests(TestCase): class NetscapeSPKITests(TestCase, _PKeyInteractionTestsMixin): """ - Tests for L{OpenSSL.crypto.NetscapeSPKI}. + Tests for :py:obj:`OpenSSL.crypto.NetscapeSPKI`. """ def signable(self): """ - Return a new L{NetscapeSPKI} for use with signing tests. + Return a new :py:obj:`NetscapeSPKI` for use with signing tests. """ return NetscapeSPKI() def test_type(self): """ - L{NetscapeSPKI} and L{NetscapeSPKIType} refer to the same type object + :py:obj:`NetscapeSPKI` and :py:obj:`NetscapeSPKIType` refer to the same type object and can be used to create instances of that type. """ self.assertIdentical(NetscapeSPKI, NetscapeSPKIType) @@ -2316,7 +2633,7 @@ class NetscapeSPKITests(TestCase, _PKeyInteractionTestsMixin): def test_construction(self): """ - L{NetscapeSPKI} returns an instance of L{NetscapeSPKIType}. + :py:obj:`NetscapeSPKI` returns an instance of :py:obj:`NetscapeSPKIType`. """ nspki = NetscapeSPKI() self.assertTrue(isinstance(nspki, NetscapeSPKIType)) @@ -2324,8 +2641,8 @@ class NetscapeSPKITests(TestCase, _PKeyInteractionTestsMixin): def test_invalid_attribute(self): """ - Accessing a non-existent attribute of a L{NetscapeSPKI} instance causes - an L{AttributeError} to be raised. + Accessing a non-existent attribute of a :py:obj:`NetscapeSPKI` instance causes + an :py:obj:`AttributeError` to be raised. """ nspki = NetscapeSPKI() self.assertRaises(AttributeError, lambda: nspki.foo) @@ -2333,21 +2650,21 @@ class NetscapeSPKITests(TestCase, _PKeyInteractionTestsMixin): def test_b64_encode(self): """ - L{NetscapeSPKI.b64_encode} encodes the certificate to a base64 blob. + :py:obj:`NetscapeSPKI.b64_encode` encodes the certificate to a base64 blob. """ nspki = NetscapeSPKI() blob = nspki.b64_encode() - self.assertTrue(isinstance(blob, bytes)) + self.assertTrue(isinstance(blob, binary_type)) class RevokedTests(TestCase): """ - Tests for L{OpenSSL.crypto.Revoked} + Tests for :py:obj:`OpenSSL.crypto.Revoked` """ def test_construction(self): """ - Confirm we can create L{OpenSSL.crypto.Revoked}. Check + Confirm we can create :py:obj:`OpenSSL.crypto.Revoked`. Check that it is empty. """ revoked = Revoked() @@ -2360,8 +2677,8 @@ class RevokedTests(TestCase): def test_construction_wrong_args(self): """ - Calling L{OpenSSL.crypto.Revoked} with any arguments results - in a L{TypeError} being raised. + Calling :py:obj:`OpenSSL.crypto.Revoked` with any arguments results + in a :py:obj:`TypeError` being raised. """ self.assertRaises(TypeError, Revoked, None) self.assertRaises(TypeError, Revoked, 1) @@ -2371,7 +2688,7 @@ class RevokedTests(TestCase): def test_serial(self): """ Confirm we can set and get serial numbers from - L{OpenSSL.crypto.Revoked}. Confirm errors are handled + :py:obj:`OpenSSL.crypto.Revoked`. Confirm errors are handled with grace. """ revoked = Revoked() @@ -2394,7 +2711,7 @@ class RevokedTests(TestCase): def test_date(self): """ Confirm we can set and get revocation dates from - L{OpenSSL.crypto.Revoked}. Confirm errors are handled + :py:obj:`OpenSSL.crypto.Revoked`. Confirm errors are handled with grace. """ revoked = Revoked() @@ -2411,7 +2728,7 @@ class RevokedTests(TestCase): def test_reason(self): """ Confirm we can set and get revocation reasons from - L{OpenSSL.crypto.Revoked}. The "get" need to work + :py:obj:`OpenSSL.crypto.Revoked`. The "get" need to work as "set". Likewise, each reason of all_reasons() must work. """ revoked = Revoked() @@ -2431,9 +2748,9 @@ class RevokedTests(TestCase): def test_set_reason_wrong_arguments(self): """ - Calling L{OpenSSL.crypto.Revoked.set_reason} with other than + Calling :py:obj:`OpenSSL.crypto.Revoked.set_reason` with other than one argument, or an argument which isn't a valid reason, - results in L{TypeError} or L{ValueError} being raised. + results in :py:obj:`TypeError` or :py:obj:`ValueError` being raised. """ revoked = Revoked() self.assertRaises(TypeError, revoked.set_reason, 100) @@ -2442,8 +2759,8 @@ class RevokedTests(TestCase): def test_get_reason_wrong_arguments(self): """ - Calling L{OpenSSL.crypto.Revoked.get_reason} with any - arguments results in L{TypeError} being raised. + Calling :py:obj:`OpenSSL.crypto.Revoked.get_reason` with any + arguments results in :py:obj:`TypeError` being raised. """ revoked = Revoked() self.assertRaises(TypeError, revoked.get_reason, None) @@ -2454,14 +2771,14 @@ class RevokedTests(TestCase): class CRLTests(TestCase): """ - Tests for L{OpenSSL.crypto.CRL} + Tests for :py:obj:`OpenSSL.crypto.CRL` """ cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM) pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM) def test_construction(self): """ - Confirm we can create L{OpenSSL.crypto.CRL}. Check + Confirm we can create :py:obj:`OpenSSL.crypto.CRL`. Check that it is empty """ crl = CRL() @@ -2471,8 +2788,8 @@ class CRLTests(TestCase): def test_construction_wrong_args(self): """ - Calling L{OpenSSL.crypto.CRL} with any number of arguments - results in a L{TypeError} being raised. + Calling :py:obj:`OpenSSL.crypto.CRL` with any number of arguments + results in a :py:obj:`TypeError` being raised. """ self.assertRaises(TypeError, CRL, 1) self.assertRaises(TypeError, CRL, "") @@ -2495,14 +2812,14 @@ class CRLTests(TestCase): # PEM format dumped_crl = crl.export(self.cert, self.pkey, days=20) - text = _runopenssl(dumped_crl, "crl", "-noout", "-text") + text = _runopenssl(dumped_crl, b"crl", b"-noout", b"-text") text.index(b('Serial Number: 03AB')) text.index(b('Superseded')) text.index(b('Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=Testing Root CA')) # DER format dumped_crl = crl.export(self.cert, self.pkey, FILETYPE_ASN1) - text = _runopenssl(dumped_crl, "crl", "-noout", "-text", "-inform", "DER") + text = _runopenssl(dumped_crl, b"crl", b"-noout", b"-text", b"-inform", b"DER") text.index(b('Serial Number: 03AB')) text.index(b('Superseded')) text.index(b('Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=Testing Root CA')) @@ -2512,10 +2829,19 @@ class CRLTests(TestCase): self.assertEqual(text, dumped_text) + def test_export_invalid(self): + """ + If :py:obj:`CRL.export` is used with an uninitialized :py:obj:`X509` + instance, :py:obj:`OpenSSL.crypto.Error` is raised. + """ + crl = CRL() + self.assertRaises(Error, crl.export, X509(), PKey()) + + def test_add_revoked_keyword(self): """ - L{OpenSSL.CRL.add_revoked} accepts its single argument as the - I{revoked} keyword argument. + :py:obj:`OpenSSL.CRL.add_revoked` accepts its single argument as the + ``revoked`` keyword argument. """ crl = CRL() revoked = Revoked() @@ -2525,10 +2851,10 @@ class CRLTests(TestCase): def test_export_wrong_args(self): """ - Calling L{OpenSSL.CRL.export} with fewer than two or more than + Calling :py:obj:`OpenSSL.CRL.export` with fewer than two or more than four arguments, or with arguments other than the certificate, private key, integer file type, and integer number of days it - expects, results in a L{TypeError} being raised. + expects, results in a :py:obj:`TypeError` being raised. """ crl = CRL() self.assertRaises(TypeError, crl.export) @@ -2543,9 +2869,9 @@ class CRLTests(TestCase): def test_export_unknown_filetype(self): """ - Calling L{OpenSSL.CRL.export} with a file type other than - L{FILETYPE_PEM}, L{FILETYPE_ASN1}, or L{FILETYPE_TEXT} results - in a L{ValueError} being raised. + Calling :py:obj:`OpenSSL.CRL.export` with a file type other than + :py:obj:`FILETYPE_PEM`, :py:obj:`FILETYPE_ASN1`, or :py:obj:`FILETYPE_TEXT` results + in a :py:obj:`ValueError` being raised. """ crl = CRL() self.assertRaises(ValueError, crl.export, self.cert, self.pkey, 100, 10) @@ -2554,7 +2880,7 @@ class CRLTests(TestCase): def test_get_revoked(self): """ Use python to create a simple CRL with two revocations. - Get back the L{Revoked} using L{OpenSSL.CRL.get_revoked} and + Get back the :py:obj:`Revoked` using :py:obj:`OpenSSL.CRL.get_revoked` and verify them. """ crl = CRL() @@ -2580,8 +2906,8 @@ class CRLTests(TestCase): def test_get_revoked_wrong_args(self): """ - Calling L{OpenSSL.CRL.get_revoked} with any arguments results - in a L{TypeError} being raised. + Calling :py:obj:`OpenSSL.CRL.get_revoked` with any arguments results + in a :py:obj:`TypeError` being raised. """ crl = CRL() self.assertRaises(TypeError, crl.get_revoked, None) @@ -2592,8 +2918,8 @@ class CRLTests(TestCase): def test_add_revoked_wrong_args(self): """ - Calling L{OpenSSL.CRL.add_revoked} with other than one - argument results in a L{TypeError} being raised. + Calling :py:obj:`OpenSSL.CRL.add_revoked` with other than one + argument results in a :py:obj:`TypeError` being raised. """ crl = CRL() self.assertRaises(TypeError, crl.add_revoked) @@ -2614,7 +2940,7 @@ class CRLTests(TestCase): self.assertEqual(revs[1].get_serial(), b('0100')) self.assertEqual(revs[1].get_reason(), b('Superseded')) - der = _runopenssl(crlData, "crl", "-outform", "DER") + der = _runopenssl(crlData, b"crl", b"-outform", b"DER") crl = load_crl(FILETYPE_ASN1, der) revs = crl.get_revoked() self.assertEqual(len(revs), 2) @@ -2626,8 +2952,8 @@ class CRLTests(TestCase): def test_load_crl_wrong_args(self): """ - Calling L{OpenSSL.crypto.load_crl} with other than two - arguments results in a L{TypeError} being raised. + Calling :py:obj:`OpenSSL.crypto.load_crl` with other than two + arguments results in a :py:obj:`TypeError` being raised. """ self.assertRaises(TypeError, load_crl) self.assertRaises(TypeError, load_crl, FILETYPE_PEM) @@ -2636,27 +2962,28 @@ class CRLTests(TestCase): def test_load_crl_bad_filetype(self): """ - Calling L{OpenSSL.crypto.load_crl} with an unknown file type - raises a L{ValueError}. + Calling :py:obj:`OpenSSL.crypto.load_crl` with an unknown file type + raises a :py:obj:`ValueError`. """ self.assertRaises(ValueError, load_crl, 100, crlData) def test_load_crl_bad_data(self): """ - Calling L{OpenSSL.crypto.load_crl} with file data which can't - be loaded raises a L{OpenSSL.crypto.Error}. + Calling :py:obj:`OpenSSL.crypto.load_crl` with file data which can't + be loaded raises a :py:obj:`OpenSSL.crypto.Error`. """ - self.assertRaises(Error, load_crl, FILETYPE_PEM, "hello, world") + self.assertRaises(Error, load_crl, FILETYPE_PEM, b"hello, world") + class SignVerifyTests(TestCase): """ - Tests for L{OpenSSL.crypto.sign} and L{OpenSSL.crypto.verify}. + Tests for :py:obj:`OpenSSL.crypto.sign` and :py:obj:`OpenSSL.crypto.verify`. """ def test_sign_verify(self): """ - L{sign} generates a cryptographic signature which L{verify} can check. + :py:obj:`sign` generates a cryptographic signature which :py:obj:`verify` can check. """ content = b( "It was a bright cold day in April, and the clocks were striking " @@ -2697,7 +3024,7 @@ class SignVerifyTests(TestCase): def test_sign_nulls(self): """ - L{sign} produces a signature for a string with embedded nulls. + :py:obj:`sign` produces a signature for a string with embedded nulls. """ content = b("Watch out! \0 Did you see it?") priv_key = load_privatekey(FILETYPE_PEM, root_key_pem) diff --git a/OpenSSL/test/test_rand.py b/OpenSSL/test/test_rand.py index 00fc6d1..c52cb6b 100644 --- a/OpenSSL/test/test_rand.py +++ b/OpenSSL/test/test_rand.py @@ -2,12 +2,13 @@ # See LICENSE for details. """ -Unit tests for L{OpenSSL.rand}. +Unit tests for :py:obj:`OpenSSL.rand`. """ from unittest import main import os import stat +import sys from OpenSSL.test.util import TestCase, b from OpenSSL import rand @@ -16,14 +17,21 @@ from OpenSSL import rand class RandTests(TestCase): def test_bytes_wrong_args(self): """ - L{OpenSSL.rand.bytes} raises L{TypeError} if called with the wrong - number of arguments or with a non-C{int} argument. + :py:obj:`OpenSSL.rand.bytes` raises :py:obj:`TypeError` if called with the wrong + number of arguments or with a non-:py:obj:`int` argument. """ self.assertRaises(TypeError, rand.bytes) self.assertRaises(TypeError, rand.bytes, None) self.assertRaises(TypeError, rand.bytes, 3, None) - # XXX Test failure of the malloc() in rand_bytes. + + def test_insufficientMemory(self): + """ + :py:obj:`OpenSSL.rand.bytes` raises :py:obj:`MemoryError` if more bytes + are requested than will fit in memory. + """ + self.assertRaises(MemoryError, rand.bytes, sys.maxsize) + def test_bytes(self): """ @@ -44,7 +52,7 @@ class RandTests(TestCase): def test_add_wrong_args(self): """ When called with the wrong number of arguments, or with arguments not of - type C{str} and C{int}, L{OpenSSL.rand.add} raises L{TypeError}. + type :py:obj:`str` and :py:obj:`int`, :py:obj:`OpenSSL.rand.add` raises :py:obj:`TypeError`. """ self.assertRaises(TypeError, rand.add) self.assertRaises(TypeError, rand.add, b("foo"), None) @@ -54,15 +62,15 @@ class RandTests(TestCase): def test_add(self): """ - L{OpenSSL.rand.add} adds entropy to the PRNG. + :py:obj:`OpenSSL.rand.add` adds entropy to the PRNG. """ rand.add(b('hamburger'), 3) def test_seed_wrong_args(self): """ - When called with the wrong number of arguments, or with a non-C{str} - argument, L{OpenSSL.rand.seed} raises L{TypeError}. + When called with the wrong number of arguments, or with a non-:py:obj:`str` + argument, :py:obj:`OpenSSL.rand.seed` raises :py:obj:`TypeError`. """ self.assertRaises(TypeError, rand.seed) self.assertRaises(TypeError, rand.seed, None) @@ -71,14 +79,14 @@ class RandTests(TestCase): def test_seed(self): """ - L{OpenSSL.rand.seed} adds entropy to the PRNG. + :py:obj:`OpenSSL.rand.seed` adds entropy to the PRNG. """ rand.seed(b('milk shake')) def test_status_wrong_args(self): """ - L{OpenSSL.rand.status} raises L{TypeError} when called with any + :py:obj:`OpenSSL.rand.status` raises :py:obj:`TypeError` when called with any arguments. """ self.assertRaises(TypeError, rand.status, None) @@ -86,8 +94,8 @@ class RandTests(TestCase): def test_status(self): """ - L{OpenSSL.rand.status} returns C{True} if the PRNG has sufficient - entropy, C{False} otherwise. + :py:obj:`OpenSSL.rand.status` returns :py:obj:`True` if the PRNG has sufficient + entropy, :py:obj:`False` otherwise. """ # It's hard to know what it is actually going to return. Different # OpenSSL random engines decide differently whether they have enough @@ -97,8 +105,8 @@ class RandTests(TestCase): def test_egd_wrong_args(self): """ - L{OpenSSL.rand.egd} raises L{TypeError} when called with the wrong - number of arguments or with arguments not of type C{str} and C{int}. + :py:obj:`OpenSSL.rand.egd` raises :py:obj:`TypeError` when called with the wrong + number of arguments or with arguments not of type :py:obj:`str` and :py:obj:`int`. """ self.assertRaises(TypeError, rand.egd) self.assertRaises(TypeError, rand.egd, None) @@ -109,8 +117,8 @@ class RandTests(TestCase): def test_egd_missing(self): """ - L{OpenSSL.rand.egd} returns C{0} or C{-1} if the EGD socket passed - to it does not exist. + :py:obj:`OpenSSL.rand.egd` returns :py:obj:`0` or :py:obj:`-1` if the + EGD socket passed to it does not exist. """ result = rand.egd(self.mktemp()) expected = (-1, 0) @@ -119,9 +127,22 @@ class RandTests(TestCase): "%r not in %r" % (result, expected)) + def test_egd_missing_and_bytes(self): + """ + :py:obj:`OpenSSL.rand.egd` returns :py:obj:`0` or :py:obj:`-1` if the + EGD socket passed to it does not exist even if a size argument is + explicitly passed. + """ + result = rand.egd(self.mktemp(), 1024) + expected = (-1, 0) + self.assertTrue( + result in expected, + "%r not in %r" % (result, expected)) + + def test_cleanup_wrong_args(self): """ - L{OpenSSL.rand.cleanup} raises L{TypeError} when called with any + :py:obj:`OpenSSL.rand.cleanup` raises :py:obj:`TypeError` when called with any arguments. """ self.assertRaises(TypeError, rand.cleanup, None) @@ -129,16 +150,16 @@ class RandTests(TestCase): def test_cleanup(self): """ - L{OpenSSL.rand.cleanup} releases the memory used by the PRNG and returns - C{None}. + :py:obj:`OpenSSL.rand.cleanup` releases the memory used by the PRNG and returns + :py:obj:`None`. """ self.assertIdentical(rand.cleanup(), None) def test_load_file_wrong_args(self): """ - L{OpenSSL.rand.load_file} raises L{TypeError} when called the wrong - number of arguments or arguments not of type C{str} and C{int}. + :py:obj:`OpenSSL.rand.load_file` raises :py:obj:`TypeError` when called the wrong + number of arguments or arguments not of type :py:obj:`str` and :py:obj:`int`. """ self.assertRaises(TypeError, rand.load_file) self.assertRaises(TypeError, rand.load_file, "foo", None) @@ -148,8 +169,8 @@ class RandTests(TestCase): def test_write_file_wrong_args(self): """ - L{OpenSSL.rand.write_file} raises L{TypeError} when called with the - wrong number of arguments or a non-C{str} argument. + :py:obj:`OpenSSL.rand.write_file` raises :py:obj:`TypeError` when called with the + wrong number of arguments or a non-:py:obj:`str` argument. """ self.assertRaises(TypeError, rand.write_file) self.assertRaises(TypeError, rand.write_file, None) @@ -169,7 +190,7 @@ class RandTests(TestCase): rand.write_file(tmpfile) # Verify length of written file size = os.stat(tmpfile)[stat.ST_SIZE] - self.assertEquals(size, 1024) + self.assertEqual(1024, size) # Read random bytes from file rand.load_file(tmpfile) rand.load_file(tmpfile, 4) # specify a length diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index 2ab67fd..a6f0127 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -2,35 +2,46 @@ # See LICENSE for details. """ -Unit tests for L{OpenSSL.SSL}. +Unit tests for :py:obj:`OpenSSL.SSL`. """ -from gc import collect -from errno import ECONNREFUSED, EINPROGRESS, EWOULDBLOCK +from gc import collect, get_referrers +from errno import ECONNREFUSED, EINPROGRESS, EWOULDBLOCK, EPIPE, ESHUTDOWN from sys import platform, version_info -from socket import error, socket +from socket import SHUT_RDWR, error, socket from os import makedirs from os.path import join from unittest import main from weakref import ref +from six import PY3, u + from OpenSSL.crypto import TYPE_RSA, FILETYPE_PEM -from OpenSSL.crypto import PKey, X509, X509Extension +from OpenSSL.crypto import PKey, X509, X509Extension, X509Store from OpenSSL.crypto import dump_privatekey, load_privatekey from OpenSSL.crypto import dump_certificate, load_certificate from OpenSSL.SSL import OPENSSL_VERSION_NUMBER, SSLEAY_VERSION, SSLEAY_CFLAGS from OpenSSL.SSL import SSLEAY_PLATFORM, SSLEAY_DIR, SSLEAY_BUILT_ON from OpenSSL.SSL import SENT_SHUTDOWN, RECEIVED_SHUTDOWN -from OpenSSL.SSL import SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, TLSv1_METHOD -from OpenSSL.SSL import OP_NO_SSLv2, OP_NO_SSLv3, OP_SINGLE_DH_USE +from OpenSSL.SSL import ( + SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, TLSv1_METHOD, + TLSv1_1_METHOD, TLSv1_2_METHOD) +from OpenSSL.SSL import OP_SINGLE_DH_USE, OP_NO_SSLv2, OP_NO_SSLv3 from OpenSSL.SSL import ( VERIFY_PEER, VERIFY_FAIL_IF_NO_PEER_CERT, VERIFY_CLIENT_ONCE, VERIFY_NONE) + +from OpenSSL.SSL import ( + SESS_CACHE_OFF, SESS_CACHE_CLIENT, SESS_CACHE_SERVER, SESS_CACHE_BOTH, + SESS_CACHE_NO_AUTO_CLEAR, SESS_CACHE_NO_INTERNAL_LOOKUP, + SESS_CACHE_NO_INTERNAL_STORE, SESS_CACHE_NO_INTERNAL) + +from OpenSSL.SSL import ( + Error, SysCallError, WantReadError, WantWriteError, ZeroReturnError) from OpenSSL.SSL import ( - Error, SysCallError, WantReadError, ZeroReturnError, SSLeay_version) -from OpenSSL.SSL import Context, ContextType, Connection, ConnectionType + Context, ContextType, Session, Connection, ConnectionType, SSLeay_version) -from OpenSSL.test.util import TestCase, bytes, b +from OpenSSL.test.util import TestCase, b from OpenSSL.test.test_crypto import ( cleartextCertificatePEM, cleartextPrivateKeyPEM) from OpenSSL.test.test_crypto import ( @@ -50,6 +61,21 @@ try: except ImportError: OP_NO_TICKET = None +try: + from OpenSSL.SSL import OP_NO_COMPRESSION +except ImportError: + OP_NO_COMPRESSION = None + +try: + from OpenSSL.SSL import MODE_RELEASE_BUFFERS +except ImportError: + MODE_RELEASE_BUFFERS = None + +try: + from OpenSSL.SSL import OP_NO_TLSv1, OP_NO_TLSv1_1, OP_NO_TLSv1_2 +except ImportError: + OP_NO_TLSv1 = OP_NO_TLSv1_1 = OP_NO_TLSv1_2 = None + from OpenSSL.SSL import ( SSL_ST_CONNECT, SSL_ST_ACCEPT, SSL_ST_MASK, SSL_ST_INIT, SSL_ST_BEFORE, SSL_ST_OK, SSL_ST_RENEGOTIATE, @@ -172,16 +198,30 @@ class _LoopbackMixin: Helper mixin which defines methods for creating a connected socket pair and for forcing two connected SSL sockets to talk to each other via memory BIOs. """ - def _loopback(self): - (server, client) = socket_pair() + def _loopbackClientFactory(self, socket): + client = Connection(Context(TLSv1_METHOD), socket) + client.set_connect_state() + return client + + def _loopbackServerFactory(self, socket): ctx = Context(TLSv1_METHOD) ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem)) ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem)) - server = Connection(ctx, server) + server = Connection(ctx, socket) server.set_accept_state() - client = Connection(Context(TLSv1_METHOD), client) - client.set_connect_state() + return server + + + def _loopback(self, serverFactory=None, clientFactory=None): + if serverFactory is None: + serverFactory = self._loopbackServerFactory + if clientFactory is None: + clientFactory = self._loopbackClientFactory + + (server, client) = socket_pair() + server = serverFactory(server) + client = clientFactory(client) handshake(client, server) @@ -192,10 +232,10 @@ class _LoopbackMixin: def _interactInMemory(self, client_conn, server_conn): """ - Try to read application bytes from each of the two L{Connection} + Try to read application bytes from each of the two :py:obj:`Connection` objects. Copy bytes back and forth between their send/receive buffers for as long as there is anything to copy. When there is nothing more - to copy, return C{None}. If one of them actually manages to deliver + to copy, return :py:obj:`None`. If one of them actually manages to deliver some application bytes, return a two-tuple of the connection from which the bytes were read and the bytes themselves. """ @@ -241,12 +281,12 @@ class _LoopbackMixin: class VersionTests(TestCase): """ Tests for version information exposed by - L{OpenSSL.SSL.SSLeay_version} and - L{OpenSSL.SSL.OPENSSL_VERSION_NUMBER}. + :py:obj:`OpenSSL.SSL.SSLeay_version` and + :py:obj:`OpenSSL.SSL.OPENSSL_VERSION_NUMBER`. """ def test_OPENSSL_VERSION_NUMBER(self): """ - L{OPENSSL_VERSION_NUMBER} is an integer with status in the low + :py:obj:`OPENSSL_VERSION_NUMBER` is an integer with status in the low byte and the patch, fix, minor, and major versions in the nibbles above that. """ @@ -255,7 +295,7 @@ class VersionTests(TestCase): def test_SSLeay_version(self): """ - L{SSLeay_version} takes a version type indicator and returns + :py:obj:`SSLeay_version` takes a version type indicator and returns one of a number of version strings based on that indicator. """ versions = {} @@ -270,30 +310,46 @@ class VersionTests(TestCase): class ContextTests(TestCase, _LoopbackMixin): """ - Unit tests for L{OpenSSL.SSL.Context}. + Unit tests for :py:obj:`OpenSSL.SSL.Context`. """ def test_method(self): """ - L{Context} can be instantiated with one of L{SSLv2_METHOD}, - L{SSLv3_METHOD}, L{SSLv23_METHOD}, or L{TLSv1_METHOD}. + :py:obj:`Context` can be instantiated with one of :py:obj:`SSLv2_METHOD`, + :py:obj:`SSLv3_METHOD`, :py:obj:`SSLv23_METHOD`, :py:obj:`TLSv1_METHOD`, + :py:obj:`TLSv1_1_METHOD`, or :py:obj:`TLSv1_2_METHOD`. """ - for meth in [SSLv3_METHOD, SSLv23_METHOD, TLSv1_METHOD]: + methods = [ + SSLv3_METHOD, SSLv23_METHOD, TLSv1_METHOD] + for meth in methods: Context(meth) - try: - Context(SSLv2_METHOD) - except ValueError: - # Some versions of OpenSSL have SSLv2, some don't. - # Difficult to say in advance. - pass + + maybe = [SSLv2_METHOD, TLSv1_1_METHOD, TLSv1_2_METHOD] + for meth in maybe: + try: + Context(meth) + except (Error, ValueError): + # Some versions of OpenSSL have SSLv2 / TLSv1.1 / TLSv1.2, some + # don't. Difficult to say in advance. + pass self.assertRaises(TypeError, Context, "") self.assertRaises(ValueError, Context, 10) + if not PY3: + def test_method_long(self): + """ + On Python 2 :py:class:`Context` accepts values of type + :py:obj:`long` as well as :py:obj:`int`. + """ + Context(long(TLSv1_METHOD)) + + + def test_type(self): """ - L{Context} and L{ContextType} refer to the same type object and can be + :py:obj:`Context` and :py:obj:`ContextType` refer to the same type object and can be used to create instances of that type. """ self.assertIdentical(Context, ContextType) @@ -302,7 +358,7 @@ class ContextTests(TestCase, _LoopbackMixin): def test_use_privatekey(self): """ - L{Context.use_privatekey} takes an L{OpenSSL.crypto.PKey} instance. + :py:obj:`Context.use_privatekey` takes an :py:obj:`OpenSSL.crypto.PKey` instance. """ key = PKey() key.generate_key(TYPE_RSA, 128) @@ -311,9 +367,130 @@ class ContextTests(TestCase, _LoopbackMixin): self.assertRaises(TypeError, ctx.use_privatekey, "") + def test_use_privatekey_file_missing(self): + """ + :py:obj:`Context.use_privatekey_file` raises :py:obj:`OpenSSL.SSL.Error` + when passed the name of a file which does not exist. + """ + ctx = Context(TLSv1_METHOD) + self.assertRaises(Error, ctx.use_privatekey_file, self.mktemp()) + + + if not PY3: + def test_use_privatekey_file_long(self): + """ + On Python 2 :py:obj:`Context.use_privatekey_file` accepts a + filetype of type :py:obj:`long` as well as :py:obj:`int`. + """ + pemfile = self.mktemp() + + key = PKey() + key.generate_key(TYPE_RSA, 128) + + with open(pemfile, "wt") as pem: + pem.write( + dump_privatekey(FILETYPE_PEM, key).decode("ascii")) + + ctx = Context(TLSv1_METHOD) + ctx.use_privatekey_file(pemfile, long(FILETYPE_PEM)) + + + def test_use_certificate_wrong_args(self): + """ + :py:obj:`Context.use_certificate_wrong_args` raises :py:obj:`TypeError` + when not passed exactly one :py:obj:`OpenSSL.crypto.X509` instance as an + argument. + """ + ctx = Context(TLSv1_METHOD) + self.assertRaises(TypeError, ctx.use_certificate) + self.assertRaises(TypeError, ctx.use_certificate, "hello, world") + self.assertRaises(TypeError, ctx.use_certificate, X509(), "hello, world") + + + def test_use_certificate_uninitialized(self): + """ + :py:obj:`Context.use_certificate` raises :py:obj:`OpenSSL.SSL.Error` + when passed a :py:obj:`OpenSSL.crypto.X509` instance which has not been + initialized (ie, which does not actually have any certificate data). + """ + ctx = Context(TLSv1_METHOD) + self.assertRaises(Error, ctx.use_certificate, X509()) + + + def test_use_certificate(self): + """ + :py:obj:`Context.use_certificate` sets the certificate which will be + used to identify connections created using the context. + """ + # TODO + # Hard to assert anything. But we could set a privatekey then ask + # OpenSSL if the cert and key agree using check_privatekey. Then as + # long as check_privatekey works right we're good... + ctx = Context(TLSv1_METHOD) + ctx.use_certificate(load_certificate(FILETYPE_PEM, cleartextCertificatePEM)) + + + def test_use_certificate_file_wrong_args(self): + """ + :py:obj:`Context.use_certificate_file` raises :py:obj:`TypeError` if + called with zero arguments or more than two arguments, or if the first + argument is not a byte string or the second argumnent is not an integer. + """ + ctx = Context(TLSv1_METHOD) + self.assertRaises(TypeError, ctx.use_certificate_file) + self.assertRaises(TypeError, ctx.use_certificate_file, b"somefile", object()) + self.assertRaises( + TypeError, ctx.use_certificate_file, b"somefile", FILETYPE_PEM, object()) + self.assertRaises( + TypeError, ctx.use_certificate_file, object(), FILETYPE_PEM) + self.assertRaises( + TypeError, ctx.use_certificate_file, b"somefile", object()) + + + def test_use_certificate_file_missing(self): + """ + :py:obj:`Context.use_certificate_file` raises + `:py:obj:`OpenSSL.SSL.Error` if passed the name of a file which does not + exist. + """ + ctx = Context(TLSv1_METHOD) + self.assertRaises(Error, ctx.use_certificate_file, self.mktemp()) + + + def test_use_certificate_file(self): + """ + :py:obj:`Context.use_certificate` sets the certificate which will be + used to identify connections created using the context. + """ + # TODO + # Hard to assert anything. But we could set a privatekey then ask + # OpenSSL if the cert and key agree using check_privatekey. Then as + # long as check_privatekey works right we're good... + pem_filename = self.mktemp() + with open(pem_filename, "wb") as pem_file: + pem_file.write(cleartextCertificatePEM) + + ctx = Context(TLSv1_METHOD) + ctx.use_certificate_file(pem_filename) + + + if not PY3: + def test_use_certificate_file_long(self): + """ + On Python 2 :py:obj:`Context.use_certificate_file` accepts a + filetype of type :py:obj:`long` as well as :py:obj:`int`. + """ + pem_filename = self.mktemp() + with open(pem_filename, "wb") as pem_file: + pem_file.write(cleartextCertificatePEM) + + ctx = Context(TLSv1_METHOD) + ctx.use_certificate_file(pem_filename, long(FILETYPE_PEM)) + + def test_set_app_data_wrong_args(self): """ - L{Context.set_app_data} raises L{TypeError} if called with other than + :py:obj:`Context.set_app_data` raises :py:obj:`TypeError` if called with other than one argument. """ context = Context(TLSv1_METHOD) @@ -323,7 +500,7 @@ class ContextTests(TestCase, _LoopbackMixin): def test_get_app_data_wrong_args(self): """ - L{Context.get_app_data} raises L{TypeError} if called with any + :py:obj:`Context.get_app_data` raises :py:obj:`TypeError` if called with any arguments. """ context = Context(TLSv1_METHOD) @@ -332,8 +509,8 @@ class ContextTests(TestCase, _LoopbackMixin): def test_app_data(self): """ - L{Context.set_app_data} stores an object for later retrieval using - L{Context.get_app_data}. + :py:obj:`Context.set_app_data` stores an object for later retrieval using + :py:obj:`Context.get_app_data`. """ app_data = object() context = Context(TLSv1_METHOD) @@ -343,8 +520,8 @@ class ContextTests(TestCase, _LoopbackMixin): def test_set_options_wrong_args(self): """ - L{Context.set_options} raises L{TypeError} if called with the wrong - number of arguments or a non-C{int} argument. + :py:obj:`Context.set_options` raises :py:obj:`TypeError` if called with the wrong + number of arguments or a non-:py:obj:`int` argument. """ context = Context(TLSv1_METHOD) self.assertRaises(TypeError, context.set_options) @@ -352,10 +529,64 @@ class ContextTests(TestCase, _LoopbackMixin): self.assertRaises(TypeError, context.set_options, 1, None) + def test_set_options(self): + """ + :py:obj:`Context.set_options` returns the new options value. + """ + context = Context(TLSv1_METHOD) + options = context.set_options(OP_NO_SSLv2) + self.assertTrue(OP_NO_SSLv2 & options) + + + if not PY3: + def test_set_options_long(self): + """ + On Python 2 :py:obj:`Context.set_options` accepts values of type + :py:obj:`long` as well as :py:obj:`int`. + """ + context = Context(TLSv1_METHOD) + options = context.set_options(long(OP_NO_SSLv2)) + self.assertTrue(OP_NO_SSLv2 & options) + + + def test_set_mode_wrong_args(self): + """ + :py:obj:`Context.set`mode} raises :py:obj:`TypeError` if called with the wrong + number of arguments or a non-:py:obj:`int` argument. + """ + context = Context(TLSv1_METHOD) + self.assertRaises(TypeError, context.set_mode) + self.assertRaises(TypeError, context.set_mode, None) + self.assertRaises(TypeError, context.set_mode, 1, None) + + + if MODE_RELEASE_BUFFERS is not None: + def test_set_mode(self): + """ + :py:obj:`Context.set_mode` accepts a mode bitvector and returns the newly + set mode. + """ + context = Context(TLSv1_METHOD) + self.assertTrue( + MODE_RELEASE_BUFFERS & context.set_mode(MODE_RELEASE_BUFFERS)) + + if not PY3: + def test_set_mode_long(self): + """ + On Python 2 :py:obj:`Context.set_mode` accepts values of type + :py:obj:`long` as well as :py:obj:`int`. + """ + context = Context(TLSv1_METHOD) + mode = context.set_mode(long(MODE_RELEASE_BUFFERS)) + self.assertTrue(MODE_RELEASE_BUFFERS & mode) + else: + "MODE_RELEASE_BUFFERS unavailable - OpenSSL version may be too old" + + def test_set_timeout_wrong_args(self): """ - L{Context.set_timeout} raises L{TypeError} if called with the wrong - number of arguments or a non-C{int} argument. + :py:obj:`Context.set_timeout` raises :py:obj:`TypeError` if called with the wrong + number of arguments or a non-:py:obj:`int` argument. """ context = Context(TLSv1_METHOD) self.assertRaises(TypeError, context.set_timeout) @@ -365,7 +596,7 @@ class ContextTests(TestCase, _LoopbackMixin): def test_get_timeout_wrong_args(self): """ - L{Context.get_timeout} raises L{TypeError} if called with any arguments. + :py:obj:`Context.get_timeout` raises :py:obj:`TypeError` if called with any arguments. """ context = Context(TLSv1_METHOD) self.assertRaises(TypeError, context.get_timeout, None) @@ -373,8 +604,8 @@ class ContextTests(TestCase, _LoopbackMixin): def test_timeout(self): """ - L{Context.set_timeout} sets the session timeout for all connections - created using the context object. L{Context.get_timeout} retrieves this + :py:obj:`Context.set_timeout` sets the session timeout for all connections + created using the context object. :py:obj:`Context.get_timeout` retrieves this value. """ context = Context(TLSv1_METHOD) @@ -382,10 +613,21 @@ class ContextTests(TestCase, _LoopbackMixin): self.assertEquals(context.get_timeout(), 1234) + if not PY3: + def test_timeout_long(self): + """ + On Python 2 :py:obj:`Context.set_timeout` accepts values of type + `long` as well as int. + """ + context = Context(TLSv1_METHOD) + context.set_timeout(long(1234)) + self.assertEquals(context.get_timeout(), 1234) + + def test_set_verify_depth_wrong_args(self): """ - L{Context.set_verify_depth} raises L{TypeError} if called with the wrong - number of arguments or a non-C{int} argument. + :py:obj:`Context.set_verify_depth` raises :py:obj:`TypeError` if called with the wrong + number of arguments or a non-:py:obj:`int` argument. """ context = Context(TLSv1_METHOD) self.assertRaises(TypeError, context.set_verify_depth) @@ -395,7 +637,7 @@ class ContextTests(TestCase, _LoopbackMixin): def test_get_verify_depth_wrong_args(self): """ - L{Context.get_verify_depth} raises L{TypeError} if called with any arguments. + :py:obj:`Context.get_verify_depth` raises :py:obj:`TypeError` if called with any arguments. """ context = Context(TLSv1_METHOD) self.assertRaises(TypeError, context.get_verify_depth, None) @@ -403,15 +645,26 @@ class ContextTests(TestCase, _LoopbackMixin): def test_verify_depth(self): """ - L{Context.set_verify_depth} sets the number of certificates in a chain + :py:obj:`Context.set_verify_depth` sets the number of certificates in a chain to follow before giving up. The value can be retrieved with - L{Context.get_verify_depth}. + :py:obj:`Context.get_verify_depth`. """ context = Context(TLSv1_METHOD) context.set_verify_depth(11) self.assertEquals(context.get_verify_depth(), 11) + if not PY3: + def test_verify_depth_long(self): + """ + On Python 2 :py:obj:`Context.set_verify_depth` accepts values of + type `long` as well as int. + """ + context = Context(TLSv1_METHOD) + context.set_verify_depth(long(11)) + self.assertEquals(context.get_verify_depth(), 11) + + def _write_encrypted_pem(self, passphrase): """ Write a new private key out to a new file, encrypted using the given @@ -429,7 +682,7 @@ class ContextTests(TestCase, _LoopbackMixin): def test_set_passwd_cb_wrong_args(self): """ - L{Context.set_passwd_cb} raises L{TypeError} if called with the + :py:obj:`Context.set_passwd_cb` raises :py:obj:`TypeError` if called with the wrong arguments or with a non-callable first argument. """ context = Context(TLSv1_METHOD) @@ -440,7 +693,7 @@ class ContextTests(TestCase, _LoopbackMixin): def test_set_passwd_cb(self): """ - L{Context.set_passwd_cb} accepts a callable which will be invoked when + :py:obj:`Context.set_passwd_cb` accepts a callable which will be invoked when a private key is loaded from an encrypted PEM. """ passphrase = b("foobar") @@ -460,7 +713,7 @@ class ContextTests(TestCase, _LoopbackMixin): def test_passwd_callback_exception(self): """ - L{Context.use_privatekey_file} propagates any exception raised by the + :py:obj:`Context.use_privatekey_file` propagates any exception raised by the passphrase callback. """ pemFile = self._write_encrypted_pem(b("monkeys are nice")) @@ -474,12 +727,12 @@ class ContextTests(TestCase, _LoopbackMixin): def test_passwd_callback_false(self): """ - L{Context.use_privatekey_file} raises L{OpenSSL.SSL.Error} if the + :py:obj:`Context.use_privatekey_file` raises :py:obj:`OpenSSL.SSL.Error` if the passphrase callback returns a false value. """ pemFile = self._write_encrypted_pem(b("monkeys are nice")) def passphraseCallback(maxlen, verify, extra): - return None + return b"" context = Context(TLSv1_METHOD) context.set_passwd_cb(passphraseCallback) @@ -488,7 +741,7 @@ class ContextTests(TestCase, _LoopbackMixin): def test_passwd_callback_non_string(self): """ - L{Context.use_privatekey_file} raises L{OpenSSL.SSL.Error} if the + :py:obj:`Context.use_privatekey_file` raises :py:obj:`OpenSSL.SSL.Error` if the passphrase callback returns a true non-string value. """ pemFile = self._write_encrypted_pem(b("monkeys are nice")) @@ -497,7 +750,7 @@ class ContextTests(TestCase, _LoopbackMixin): context = Context(TLSv1_METHOD) context.set_passwd_cb(passphraseCallback) - self.assertRaises(Error, context.use_privatekey_file, pemFile) + self.assertRaises(ValueError, context.use_privatekey_file, pemFile) def test_passwd_callback_too_long(self): @@ -521,7 +774,7 @@ class ContextTests(TestCase, _LoopbackMixin): def test_set_info_callback(self): """ - L{Context.set_info_callback} accepts a callable which will be invoked + :py:obj:`Context.set_info_callback` accepts a callable which will be invoked when certain information about an SSL connection is available. """ (server, client) = socket_pair() @@ -542,22 +795,26 @@ class ContextTests(TestCase, _LoopbackMixin): serverSSL = Connection(context, server) serverSSL.set_accept_state() - while not called: - for ssl in clientSSL, serverSSL: - try: - ssl.do_handshake() - except WantReadError: - pass + handshake(clientSSL, serverSSL) - # Kind of lame. Just make sure it got called somehow. - self.assertTrue(called) + # The callback must always be called with a Connection instance as the + # first argument. It would probably be better to split this into + # separate tests for client and server side info callbacks so we could + # assert it is called with the right Connection instance. It would + # also be good to assert *something* about `where` and `ret`. + notConnections = [ + conn for (conn, where, ret) in called + if not isinstance(conn, Connection)] + self.assertEqual( + [], notConnections, + "Some info callback arguments were not Connection instaces.") def _load_verify_locations_test(self, *args): """ Create a client context which will verify the peer certificate and call - its C{load_verify_locations} method with C{*args}. Then connect it to a - server and ensure that the handshake succeeds. + its :py:obj:`load_verify_locations` method with the given arguments. + Then connect it to a server and ensure that the handshake succeeds. """ (server, client) = socket_pair() @@ -593,7 +850,7 @@ class ContextTests(TestCase, _LoopbackMixin): def test_load_verify_file(self): """ - L{Context.load_verify_locations} accepts a file name and uses the + :py:obj:`Context.load_verify_locations` accepts a file name and uses the certificates within for verification purposes. """ cafile = self.mktemp() @@ -606,7 +863,7 @@ class ContextTests(TestCase, _LoopbackMixin): def test_load_verify_invalid_file(self): """ - L{Context.load_verify_locations} raises L{Error} when passed a + :py:obj:`Context.load_verify_locations` raises :py:obj:`Error` when passed a non-existent cafile. """ clientContext = Context(TLSv1_METHOD) @@ -616,7 +873,7 @@ class ContextTests(TestCase, _LoopbackMixin): def test_load_verify_directory(self): """ - L{Context.load_verify_locations} accepts a directory name and uses + :py:obj:`Context.load_verify_locations` accepts a directory name and uses the certificates within for verification purposes. """ capath = self.mktemp() @@ -624,7 +881,7 @@ class ContextTests(TestCase, _LoopbackMixin): # Hash values computed manually with c_rehash to avoid depending on # c_rehash in the test suite. One is from OpenSSL 0.9.8, the other # from OpenSSL 1.0.0. - for name in ['c7adac82.0', 'c3705638.0']: + for name in [b'c7adac82.0', b'c3705638.0']: cafile = join(capath, name) fObj = open(cafile, 'w') fObj.write(cleartextCertificatePEM.decode('ascii')) @@ -635,8 +892,8 @@ class ContextTests(TestCase, _LoopbackMixin): def test_load_verify_locations_wrong_args(self): """ - L{Context.load_verify_locations} raises L{TypeError} if called with - the wrong number of arguments or with non-C{str} arguments. + :py:obj:`Context.load_verify_locations` raises :py:obj:`TypeError` if called with + the wrong number of arguments or with non-:py:obj:`str` arguments. """ context = Context(TLSv1_METHOD) self.assertRaises(TypeError, context.load_verify_locations) @@ -651,7 +908,7 @@ class ContextTests(TestCase, _LoopbackMixin): else: def test_set_default_verify_paths(self): """ - L{Context.set_default_verify_paths} causes the platform-specific CA + :py:obj:`Context.set_default_verify_paths` causes the platform-specific CA certificate locations to be used for verification purposes. """ # Testing this requires a server with a certificate signed by one of @@ -674,14 +931,14 @@ class ContextTests(TestCase, _LoopbackMixin): clientSSL = Connection(context, client) clientSSL.set_connect_state() clientSSL.do_handshake() - clientSSL.send('GET / HTTP/1.0\r\n\r\n') + clientSSL.send(b"GET / HTTP/1.0\r\n\r\n") self.assertTrue(clientSSL.recv(1024)) def test_set_default_verify_paths_signature(self): """ - L{Context.set_default_verify_paths} takes no arguments and raises - L{TypeError} if given any. + :py:obj:`Context.set_default_verify_paths` takes no arguments and raises + :py:obj:`TypeError` if given any. """ context = Context(TLSv1_METHOD) self.assertRaises(TypeError, context.set_default_verify_paths, None) @@ -691,9 +948,9 @@ class ContextTests(TestCase, _LoopbackMixin): def test_add_extra_chain_cert_invalid_cert(self): """ - L{Context.add_extra_chain_cert} raises L{TypeError} if called with + :py:obj:`Context.add_extra_chain_cert` raises :py:obj:`TypeError` if called with other than one argument or if called with an object which is not an - instance of L{X509}. + instance of :py:obj:`X509`. """ context = Context(TLSv1_METHOD) self.assertRaises(TypeError, context.add_extra_chain_cert) @@ -724,12 +981,34 @@ class ContextTests(TestCase, _LoopbackMixin): pass + def test_set_verify_callback_exception(self): + """ + If the verify callback passed to :py:obj:`Context.set_verify` raises an + exception, verification fails and the exception is propagated to the + caller of :py:obj:`Connection.do_handshake`. + """ + serverContext = Context(TLSv1_METHOD) + serverContext.use_privatekey( + load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)) + serverContext.use_certificate( + load_certificate(FILETYPE_PEM, cleartextCertificatePEM)) + + clientContext = Context(TLSv1_METHOD) + def verify_callback(*args): + raise Exception("silly verify failure") + clientContext.set_verify(VERIFY_PEER, verify_callback) + + exc = self.assertRaises( + Exception, self._handshake_test, serverContext, clientContext) + self.assertEqual("silly verify failure", str(exc)) + + def test_add_extra_chain_cert(self): """ - L{Context.add_extra_chain_cert} accepts an L{X509} instance to add to + :py:obj:`Context.add_extra_chain_cert` accepts an :py:obj:`X509` instance to add to the certificate chain. - See L{_create_certificate_chain} for the details of the certificate + See :py:obj:`_create_certificate_chain` for the details of the certificate chain tested. The chain is tested by starting a server with scert and connecting @@ -762,7 +1041,7 @@ class ContextTests(TestCase, _LoopbackMixin): clientContext = Context(TLSv1_METHOD) clientContext.set_verify( VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb) - clientContext.load_verify_locations('ca.pem') + clientContext.load_verify_locations(b"ca.pem") # Try it out. self._handshake_test(serverContext, clientContext) @@ -770,7 +1049,7 @@ class ContextTests(TestCase, _LoopbackMixin): def test_use_certificate_chain_file(self): """ - L{Context.use_certificate_chain_file} reads a certificate chain from + :py:obj:`Context.use_certificate_chain_file` reads a certificate chain from the specified file. The chain is tested by starting a server with scert and connecting @@ -782,11 +1061,11 @@ class ContextTests(TestCase, _LoopbackMixin): # Write out the chain file. chainFile = self.mktemp() - fObj = open(chainFile, 'w') + fObj = open(chainFile, 'wb') # Most specific to least general. - fObj.write(dump_certificate(FILETYPE_PEM, scert).decode('ascii')) - fObj.write(dump_certificate(FILETYPE_PEM, icert).decode('ascii')) - fObj.write(dump_certificate(FILETYPE_PEM, cacert).decode('ascii')) + fObj.write(dump_certificate(FILETYPE_PEM, scert)) + fObj.write(dump_certificate(FILETYPE_PEM, icert)) + fObj.write(dump_certificate(FILETYPE_PEM, cacert)) fObj.close() serverContext = Context(TLSv1_METHOD) @@ -800,26 +1079,42 @@ class ContextTests(TestCase, _LoopbackMixin): clientContext = Context(TLSv1_METHOD) clientContext.set_verify( VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb) - clientContext.load_verify_locations('ca.pem') + clientContext.load_verify_locations(b"ca.pem") self._handshake_test(serverContext, clientContext) + + def test_use_certificate_chain_file_wrong_args(self): + """ + :py:obj:`Context.use_certificate_chain_file` raises :py:obj:`TypeError` + if passed zero or more than one argument or when passed a non-byte + string single argument. It also raises :py:obj:`OpenSSL.SSL.Error` when + passed a bad chain file name (for example, the name of a file which does + not exist). + """ + context = Context(TLSv1_METHOD) + self.assertRaises(TypeError, context.use_certificate_chain_file) + self.assertRaises(TypeError, context.use_certificate_chain_file, object()) + self.assertRaises(TypeError, context.use_certificate_chain_file, b"foo", object()) + + self.assertRaises(Error, context.use_certificate_chain_file, self.mktemp()) + # XXX load_client_ca # XXX set_session_id def test_get_verify_mode_wrong_args(self): """ - L{Context.get_verify_mode} raises L{TypeError} if called with any + :py:obj:`Context.get_verify_mode` raises :py:obj:`TypeError` if called with any arguments. """ context = Context(TLSv1_METHOD) self.assertRaises(TypeError, context.get_verify_mode, None) - def test_get_verify_mode(self): + def test_set_verify_mode(self): """ - L{Context.get_verify_mode} returns the verify mode flags previously - passed to L{Context.set_verify}. + :py:obj:`Context.get_verify_mode` returns the verify mode flags previously + passed to :py:obj:`Context.set_verify`. """ context = Context(TLSv1_METHOD) self.assertEquals(context.get_verify_mode(), 0) @@ -829,10 +1124,24 @@ class ContextTests(TestCase, _LoopbackMixin): context.get_verify_mode(), VERIFY_PEER | VERIFY_CLIENT_ONCE) + if not PY3: + def test_set_verify_mode_long(self): + """ + On Python 2 :py:obj:`Context.set_verify_mode` accepts values of + type :py:obj:`long` as well as :py:obj:`int`. + """ + context = Context(TLSv1_METHOD) + self.assertEquals(context.get_verify_mode(), 0) + context.set_verify( + long(VERIFY_PEER | VERIFY_CLIENT_ONCE), lambda *args: None) + self.assertEquals( + context.get_verify_mode(), VERIFY_PEER | VERIFY_CLIENT_ONCE) + + def test_load_tmp_dh_wrong_args(self): """ - L{Context.load_tmp_dh} raises L{TypeError} if called with the wrong - number of arguments or with a non-C{str} argument. + :py:obj:`Context.load_tmp_dh` raises :py:obj:`TypeError` if called with the wrong + number of arguments or with a non-:py:obj:`str` argument. """ context = Context(TLSv1_METHOD) self.assertRaises(TypeError, context.load_tmp_dh) @@ -842,16 +1151,16 @@ class ContextTests(TestCase, _LoopbackMixin): def test_load_tmp_dh_missing_file(self): """ - L{Context.load_tmp_dh} raises L{OpenSSL.SSL.Error} if the specified file + :py:obj:`Context.load_tmp_dh` raises :py:obj:`OpenSSL.SSL.Error` if the specified file does not exist. """ context = Context(TLSv1_METHOD) - self.assertRaises(Error, context.load_tmp_dh, "hello") + self.assertRaises(Error, context.load_tmp_dh, b"hello") def test_load_tmp_dh(self): """ - L{Context.load_tmp_dh} loads Diffie-Hellman parameters from the + :py:obj:`Context.load_tmp_dh` loads Diffie-Hellman parameters from the specified file. """ context = Context(TLSv1_METHOD) @@ -863,26 +1172,106 @@ class ContextTests(TestCase, _LoopbackMixin): # XXX What should I assert here? -exarkun - def test_set_cipher_list(self): + def test_set_cipher_list_bytes(self): """ - L{Context.set_cipher_list} accepts a C{str} naming the ciphers which - connections created with the context object will be able to choose from. + :py:obj:`Context.set_cipher_list` accepts a :py:obj:`bytes` naming the + ciphers which connections created with the context object will be able + to choose from. """ context = Context(TLSv1_METHOD) - context.set_cipher_list("hello world:EXP-RC4-MD5") + context.set_cipher_list(b"hello world:EXP-RC4-MD5") conn = Connection(context, None) self.assertEquals(conn.get_cipher_list(), ["EXP-RC4-MD5"]) + def test_set_cipher_list_text(self): + """ + :py:obj:`Context.set_cipher_list` accepts a :py:obj:`unicode` naming + the ciphers which connections created with the context object will be + able to choose from. + """ + context = Context(TLSv1_METHOD) + context.set_cipher_list(u("hello world:EXP-RC4-MD5")) + conn = Connection(context, None) + self.assertEquals(conn.get_cipher_list(), ["EXP-RC4-MD5"]) + + + def test_set_cipher_list_wrong_args(self): + """ + :py:obj:`Context.set_cipher_list` raises :py:obj:`TypeError` when + passed zero arguments or more than one argument or when passed a + non-string single argument and raises :py:obj:`OpenSSL.SSL.Error` when + passed an incorrect cipher list string. + """ + context = Context(TLSv1_METHOD) + self.assertRaises(TypeError, context.set_cipher_list) + self.assertRaises(TypeError, context.set_cipher_list, object()) + self.assertRaises(TypeError, context.set_cipher_list, b"EXP-RC4-MD5", object()) + + self.assertRaises(Error, context.set_cipher_list, "imaginary-cipher") + + + def test_set_session_cache_mode_wrong_args(self): + """ + :py:obj:`Context.set_session_cache_mode` raises :py:obj:`TypeError` if + called with other than one integer argument. + """ + context = Context(TLSv1_METHOD) + self.assertRaises(TypeError, context.set_session_cache_mode) + self.assertRaises(TypeError, context.set_session_cache_mode, object()) + + + def test_get_session_cache_mode_wrong_args(self): + """ + :py:obj:`Context.get_session_cache_mode` raises :py:obj:`TypeError` if + called with any arguments. + """ + context = Context(TLSv1_METHOD) + self.assertRaises(TypeError, context.get_session_cache_mode, 1) + + + def test_session_cache_mode(self): + """ + :py:obj:`Context.set_session_cache_mode` specifies how sessions are + cached. The setting can be retrieved via + :py:obj:`Context.get_session_cache_mode`. + """ + context = Context(TLSv1_METHOD) + context.set_session_cache_mode(SESS_CACHE_OFF) + off = context.set_session_cache_mode(SESS_CACHE_BOTH) + self.assertEqual(SESS_CACHE_OFF, off) + self.assertEqual(SESS_CACHE_BOTH, context.get_session_cache_mode()) + + if not PY3: + def test_session_cache_mode_long(self): + """ + On Python 2 :py:obj:`Context.set_session_cache_mode` accepts values + of type :py:obj:`long` as well as :py:obj:`int`. + """ + context = Context(TLSv1_METHOD) + context.set_session_cache_mode(long(SESS_CACHE_BOTH)) + self.assertEqual( + SESS_CACHE_BOTH, context.get_session_cache_mode()) + + + def test_get_cert_store(self): + """ + :py:obj:`Context.get_cert_store` returns a :py:obj:`X509Store` instance. + """ + context = Context(TLSv1_METHOD) + store = context.get_cert_store() + self.assertIsInstance(store, X509Store) + + class ServerNameCallbackTests(TestCase, _LoopbackMixin): """ - Tests for L{Context.set_tlsext_servername_callback} and its interaction with - L{Connection}. + Tests for :py:obj:`Context.set_tlsext_servername_callback` and its interaction with + :py:obj:`Connection`. """ def test_wrong_args(self): """ - L{Context.set_tlsext_servername_callback} raises L{TypeError} if called + :py:obj:`Context.set_tlsext_servername_callback` raises :py:obj:`TypeError` if called with other than one argument. """ context = Context(TLSv1_METHOD) @@ -890,9 +1279,10 @@ class ServerNameCallbackTests(TestCase, _LoopbackMixin): self.assertRaises( TypeError, context.set_tlsext_servername_callback, 1, 2) + def test_old_callback_forgotten(self): """ - If L{Context.set_tlsext_servername_callback} is used to specify a new + If :py:obj:`Context.set_tlsext_servername_callback` is used to specify a new callback, the one it replaces is dereferenced. """ def callback(connection): @@ -908,15 +1298,26 @@ class ServerNameCallbackTests(TestCase, _LoopbackMixin): del callback context.set_tlsext_servername_callback(replacement) + + # One run of the garbage collector happens to work on CPython. PyPy + # doesn't collect the underlying object until a second run for whatever + # reason. That's fine, it still demonstrates our code has properly + # dropped the reference. + collect() collect() - self.assertIdentical(None, tracker()) + + callback = tracker() + if callback is not None: + referrers = get_referrers(callback) + if len(referrers) > 1: + self.fail("Some references remain: %r" % (referrers,)) def test_no_servername(self): """ When a client specifies no server name, the callback passed to - L{Context.set_tlsext_servername_callback} is invoked and the result of - L{Connection.get_servername} is C{None}. + :py:obj:`Context.set_tlsext_servername_callback` is invoked and the result of + :py:obj:`Connection.get_servername` is :py:obj:`None`. """ args = [] def servername(conn): @@ -948,8 +1349,8 @@ class ServerNameCallbackTests(TestCase, _LoopbackMixin): def test_servername(self): """ When a client specifies a server name in its hello message, the callback - passed to L{Contexts.set_tlsext_servername_callback} is invoked and the - result of L{Connection.get_servername} is that server name. + passed to :py:obj:`Contexts.set_tlsext_servername_callback` is invoked and the + result of :py:obj:`Connection.get_servername` is that server name. """ args = [] def servername(conn): @@ -975,12 +1376,34 @@ class ServerNameCallbackTests(TestCase, _LoopbackMixin): +class SessionTests(TestCase): + """ + Unit tests for :py:obj:`OpenSSL.SSL.Session`. + """ + def test_construction(self): + """ + :py:class:`Session` can be constructed with no arguments, creating a new + instance of that type. + """ + new_session = Session() + self.assertTrue(isinstance(new_session, Session)) + + + def test_construction_wrong_args(self): + """ + If any arguments are passed to :py:class:`Session`, :py:obj:`TypeError` + is raised. + """ + self.assertRaises(TypeError, Session, 123) + self.assertRaises(TypeError, Session, "hello") + self.assertRaises(TypeError, Session, object()) + + + class ConnectionTests(TestCase, _LoopbackMixin): """ - Unit tests for L{OpenSSL.SSL.Connection}. + Unit tests for :py:obj:`OpenSSL.SSL.Connection`. """ - # XXX want_write - # XXX want_read # XXX get_peer_certificate -> None # XXX sock_shutdown # XXX master_key -> TypeError @@ -999,7 +1422,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_type(self): """ - L{Connection} and L{ConnectionType} refer to the same type object and + :py:obj:`Connection` and :py:obj:`ConnectionType` refer to the same type object and can be used to create instances of that type. """ self.assertIdentical(Connection, ConnectionType) @@ -1009,8 +1432,8 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_get_context(self): """ - L{Connection.get_context} returns the L{Context} instance used to - construct the L{Connection} instance. + :py:obj:`Connection.get_context` returns the :py:obj:`Context` instance used to + construct the :py:obj:`Connection` instance. """ context = Context(TLSv1_METHOD) connection = Connection(context, None) @@ -1019,7 +1442,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_get_context_wrong_args(self): """ - L{Connection.get_context} raises L{TypeError} if called with any + :py:obj:`Connection.get_context` raises :py:obj:`TypeError` if called with any arguments. """ connection = Connection(Context(TLSv1_METHOD), None) @@ -1028,8 +1451,8 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_set_context_wrong_args(self): """ - L{Connection.set_context} raises L{TypeError} if called with a - non-L{Context} instance argument or with any number of arguments other + :py:obj:`Connection.set_context` raises :py:obj:`TypeError` if called with a + non-:py:obj:`Context` instance argument or with any number of arguments other than 1. """ ctx = Context(TLSv1_METHOD) @@ -1046,7 +1469,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_set_context(self): """ - L{Connection.set_context} specifies a new L{Context} instance to be used + :py:obj:`Connection.set_context` specifies a new :py:obj:`Context` instance to be used for the connection. """ original = Context(SSLv23_METHOD) @@ -1062,9 +1485,9 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_set_tlsext_host_name_wrong_args(self): """ - If L{Connection.set_tlsext_host_name} is called with a non-byte string + If :py:obj:`Connection.set_tlsext_host_name` is called with a non-byte string argument or a byte string with an embedded NUL or other than one - argument, L{TypeError} is raised. + argument, :py:obj:`TypeError` is raised. """ conn = Connection(Context(TLSv1_METHOD), None) self.assertRaises(TypeError, conn.set_tlsext_host_name) @@ -1082,7 +1505,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_get_servername_wrong_args(self): """ - L{Connection.get_servername} raises L{TypeError} if called with any + :py:obj:`Connection.get_servername` raises :py:obj:`TypeError` if called with any arguments. """ connection = Connection(Context(TLSv1_METHOD), None) @@ -1093,7 +1516,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_pending(self): """ - L{Connection.pending} returns the number of bytes available for + :py:obj:`Connection.pending` returns the number of bytes available for immediate read. """ connection = Connection(Context(TLSv1_METHOD), None) @@ -1102,7 +1525,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_pending_wrong_args(self): """ - L{Connection.pending} raises L{TypeError} if called with any arguments. + :py:obj:`Connection.pending` raises :py:obj:`TypeError` if called with any arguments. """ connection = Connection(Context(TLSv1_METHOD), None) self.assertRaises(TypeError, connection.pending, None) @@ -1110,7 +1533,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_connect_wrong_args(self): """ - L{Connection.connect} raises L{TypeError} if called with a non-address + :py:obj:`Connection.connect` raises :py:obj:`TypeError` if called with a non-address argument or with the wrong number of arguments. """ connection = Connection(Context(TLSv1_METHOD), socket()) @@ -1121,7 +1544,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_connect_refused(self): """ - L{Connection.connect} raises L{socket.error} if the underlying socket + :py:obj:`Connection.connect` raises :py:obj:`socket.error` if the underlying socket connect method raises it. """ client = socket() @@ -1133,7 +1556,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_connect(self): """ - L{Connection.connect} establishes a connection to the specified address. + :py:obj:`Connection.connect` establishes a connection to the specified address. """ port = socket() port.bind(('', 0)) @@ -1149,7 +1572,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): else: def test_connect_ex(self): """ - If there is a connection error, L{Connection.connect_ex} returns the + If there is a connection error, :py:obj:`Connection.connect_ex` returns the errno instead of raising an exception. """ port = socket() @@ -1166,7 +1589,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_accept_wrong_args(self): """ - L{Connection.accept} raises L{TypeError} if called with any arguments. + :py:obj:`Connection.accept` raises :py:obj:`TypeError` if called with any arguments. """ connection = Connection(Context(TLSv1_METHOD), socket()) self.assertRaises(TypeError, connection.accept, None) @@ -1174,8 +1597,8 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_accept(self): """ - L{Connection.accept} accepts a pending connection attempt and returns a - tuple of a new L{Connection} (the accepted client) and the address the + :py:obj:`Connection.accept` accepts a pending connection attempt and returns a + tuple of a new :py:obj:`Connection` (the accepted client) and the address the connection originated from. """ ctx = Context(TLSv1_METHOD) @@ -1201,7 +1624,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_shutdown_wrong_args(self): """ - L{Connection.shutdown} raises L{TypeError} if called with the wrong + :py:obj:`Connection.shutdown` raises :py:obj:`TypeError` if called with the wrong number of arguments or with arguments other than integers. """ connection = Connection(Context(TLSv1_METHOD), None) @@ -1214,7 +1637,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_shutdown(self): """ - L{Connection.shutdown} performs an SSL-level connection shutdown. + :py:obj:`Connection.shutdown` performs an SSL-level connection shutdown. """ server, client = self._loopback() self.assertFalse(server.shutdown()) @@ -1229,7 +1652,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_set_shutdown(self): """ - L{Connection.set_shutdown} sets the state of the SSL connection shutdown + :py:obj:`Connection.set_shutdown` sets the state of the SSL connection shutdown process. """ connection = Connection(Context(TLSv1_METHOD), socket()) @@ -1237,10 +1660,21 @@ class ConnectionTests(TestCase, _LoopbackMixin): self.assertEquals(connection.get_shutdown(), RECEIVED_SHUTDOWN) + if not PY3: + def test_set_shutdown_long(self): + """ + On Python 2 :py:obj:`Connection.set_shutdown` accepts an argument + of type :py:obj:`long` as well as :py:obj:`int`. + """ + connection = Connection(Context(TLSv1_METHOD), socket()) + connection.set_shutdown(long(RECEIVED_SHUTDOWN)) + self.assertEquals(connection.get_shutdown(), RECEIVED_SHUTDOWN) + + def test_app_data_wrong_args(self): """ - L{Connection.set_app_data} raises L{TypeError} if called with other than - one argument. L{Connection.get_app_data} raises L{TypeError} if called + :py:obj:`Connection.set_app_data` raises :py:obj:`TypeError` if called with other than + one argument. :py:obj:`Connection.get_app_data` raises :py:obj:`TypeError` if called with any arguments. """ conn = Connection(Context(TLSv1_METHOD), None) @@ -1252,8 +1686,8 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_app_data(self): """ Any object can be set as app data by passing it to - L{Connection.set_app_data} and later retrieved with - L{Connection.get_app_data}. + :py:obj:`Connection.set_app_data` and later retrieved with + :py:obj:`Connection.get_app_data`. """ conn = Connection(Context(TLSv1_METHOD), None) app_data = object() @@ -1263,8 +1697,8 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_makefile(self): """ - L{Connection.makefile} is not implemented and calling that method raises - L{NotImplementedError}. + :py:obj:`Connection.makefile` is not implemented and calling that method raises + :py:obj:`NotImplementedError`. """ conn = Connection(Context(TLSv1_METHOD), None) self.assertRaises(NotImplementedError, conn.makefile) @@ -1272,7 +1706,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_get_peer_cert_chain_wrong_args(self): """ - L{Connection.get_peer_cert_chain} raises L{TypeError} if called with any + :py:obj:`Connection.get_peer_cert_chain` raises :py:obj:`TypeError` if called with any arguments. """ conn = Connection(Context(TLSv1_METHOD), None) @@ -1284,7 +1718,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_get_peer_cert_chain(self): """ - L{Connection.get_peer_cert_chain} returns a list of certificates which + :py:obj:`Connection.get_peer_cert_chain` returns a list of certificates which the connected server returned for the certification verification. """ chain = _create_certificate_chain() @@ -1318,7 +1752,7 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_get_peer_cert_chain_none(self): """ - L{Connection.get_peer_cert_chain} returns C{None} if the peer sends no + :py:obj:`Connection.get_peer_cert_chain` returns :py:obj:`None` if the peer sends no certificate chain. """ ctx = Context(TLSv1_METHOD) @@ -1332,14 +1766,181 @@ class ConnectionTests(TestCase, _LoopbackMixin): self.assertIdentical(None, server.get_peer_cert_chain()) + def test_get_session_wrong_args(self): + """ + :py:obj:`Connection.get_session` raises :py:obj:`TypeError` if called + with any arguments. + """ + ctx = Context(TLSv1_METHOD) + server = Connection(ctx, None) + self.assertRaises(TypeError, server.get_session, 123) + self.assertRaises(TypeError, server.get_session, "hello") + self.assertRaises(TypeError, server.get_session, object()) + + + def test_get_session_unconnected(self): + """ + :py:obj:`Connection.get_session` returns :py:obj:`None` when used with + an object which has not been connected. + """ + ctx = Context(TLSv1_METHOD) + server = Connection(ctx, None) + session = server.get_session() + self.assertIdentical(None, session) + + + def test_server_get_session(self): + """ + On the server side of a connection, :py:obj:`Connection.get_session` + returns a :py:class:`Session` instance representing the SSL session for + that connection. + """ + server, client = self._loopback() + session = server.get_session() + self.assertIsInstance(session, Session) + + + def test_client_get_session(self): + """ + On the client side of a connection, :py:obj:`Connection.get_session` + returns a :py:class:`Session` instance representing the SSL session for + that connection. + """ + server, client = self._loopback() + session = client.get_session() + self.assertIsInstance(session, Session) + + + def test_set_session_wrong_args(self): + """ + If called with an object that is not an instance of :py:class:`Session`, + or with other than one argument, :py:obj:`Connection.set_session` raises + :py:obj:`TypeError`. + """ + ctx = Context(TLSv1_METHOD) + connection = Connection(ctx, None) + self.assertRaises(TypeError, connection.set_session) + self.assertRaises(TypeError, connection.set_session, 123) + self.assertRaises(TypeError, connection.set_session, "hello") + self.assertRaises(TypeError, connection.set_session, object()) + self.assertRaises( + TypeError, connection.set_session, Session(), Session()) + + + def test_client_set_session(self): + """ + :py:obj:`Connection.set_session`, when used prior to a connection being + established, accepts a :py:class:`Session` instance and causes an + attempt to re-use the session it represents when the SSL handshake is + performed. + """ + key = load_privatekey(FILETYPE_PEM, server_key_pem) + cert = load_certificate(FILETYPE_PEM, server_cert_pem) + ctx = Context(TLSv1_METHOD) + ctx.use_privatekey(key) + ctx.use_certificate(cert) + ctx.set_session_id("unity-test") + + def makeServer(socket): + server = Connection(ctx, socket) + server.set_accept_state() + return server + + originalServer, originalClient = self._loopback( + serverFactory=makeServer) + originalSession = originalClient.get_session() + + def makeClient(socket): + client = self._loopbackClientFactory(socket) + client.set_session(originalSession) + return client + resumedServer, resumedClient = self._loopback( + serverFactory=makeServer, + clientFactory=makeClient) + + # This is a proxy: in general, we have no access to any unique + # identifier for the session (new enough versions of OpenSSL expose a + # hash which could be usable, but "new enough" is very, very new). + # Instead, exploit the fact that the master key is re-used if the + # session is re-used. As long as the master key for the two connections + # is the same, the session was re-used! + self.assertEqual( + originalServer.master_key(), resumedServer.master_key()) + + + def test_set_session_wrong_method(self): + """ + If :py:obj:`Connection.set_session` is passed a :py:class:`Session` + instance associated with a context using a different SSL method than the + :py:obj:`Connection` is using, a :py:class:`OpenSSL.SSL.Error` is + raised. + """ + key = load_privatekey(FILETYPE_PEM, server_key_pem) + cert = load_certificate(FILETYPE_PEM, server_cert_pem) + ctx = Context(TLSv1_METHOD) + ctx.use_privatekey(key) + ctx.use_certificate(cert) + ctx.set_session_id("unity-test") + + def makeServer(socket): + server = Connection(ctx, socket) + server.set_accept_state() + return server + + originalServer, originalClient = self._loopback( + serverFactory=makeServer) + originalSession = originalClient.get_session() + + def makeClient(socket): + # Intentionally use a different, incompatible method here. + client = Connection(Context(SSLv3_METHOD), socket) + client.set_connect_state() + client.set_session(originalSession) + return client + + self.assertRaises( + Error, + self._loopback, clientFactory=makeClient, serverFactory=makeServer) + + + def test_wantWriteError(self): + """ + :py:obj:`Connection` methods which generate output raise + :py:obj:`OpenSSL.SSL.WantWriteError` if writing to the connection's BIO + fail indicating a should-write state. + """ + client_socket, server_socket = socket_pair() + # Fill up the client's send buffer so Connection won't be able to write + # anything. + msg = b"x" * 512 + for i in range(2048): + try: + client_socket.send(msg) + except error as e: + if e.errno == EWOULDBLOCK: + break + raise + else: + self.fail( + "Failed to fill socket buffer, cannot test BIO want write") + + ctx = Context(TLSv1_METHOD) + conn = Connection(ctx, client_socket) + # Client's speak first, so make it an SSL client + conn.set_connect_state() + self.assertRaises(WantWriteError, conn.do_handshake) + + # XXX want_read + + class ConnectionGetCipherListTests(TestCase): """ - Tests for L{Connection.get_cipher_list}. + Tests for :py:obj:`Connection.get_cipher_list`. """ def test_wrong_args(self): """ - L{Connection.get_cipher_list} raises L{TypeError} if called with any + :py:obj:`Connection.get_cipher_list` raises :py:obj:`TypeError` if called with any arguments. """ connection = Connection(Context(TLSv1_METHOD), None) @@ -1348,8 +1949,8 @@ class ConnectionGetCipherListTests(TestCase): def test_result(self): """ - L{Connection.get_cipher_list} returns a C{list} of C{str} giving the - names of the ciphers which might be used. + :py:obj:`Connection.get_cipher_list` returns a :py:obj:`list` of + :py:obj:`bytes` giving the names of the ciphers which might be used. """ connection = Connection(Context(TLSv1_METHOD), None) ciphers = connection.get_cipher_list() @@ -1361,22 +1962,23 @@ class ConnectionGetCipherListTests(TestCase): class ConnectionSendTests(TestCase, _LoopbackMixin): """ - Tests for L{Connection.send} + Tests for :py:obj:`Connection.send` """ def test_wrong_args(self): """ - When called with arguments other than a single string, - L{Connection.send} raises L{TypeError}. + When called with arguments other than string argument for its first + parameter or more than two arguments, :py:obj:`Connection.send` raises + :py:obj:`TypeError`. """ connection = Connection(Context(TLSv1_METHOD), None) self.assertRaises(TypeError, connection.send) self.assertRaises(TypeError, connection.send, object()) - self.assertRaises(TypeError, connection.send, "foo", "bar") + self.assertRaises(TypeError, connection.send, "foo", object(), "bar") def test_short_bytes(self): """ - When passed a short byte string, L{Connection.send} transmits all of it + When passed a short byte string, :py:obj:`Connection.send` transmits all of it and returns the number of bytes sent. """ server, client = self._loopback() @@ -1392,7 +1994,7 @@ class ConnectionSendTests(TestCase, _LoopbackMixin): def test_short_memoryview(self): """ When passed a memoryview onto a small number of bytes, - L{Connection.send} transmits all of them and returns the number of + :py:obj:`Connection.send` transmits all of them and returns the number of bytes sent. """ server, client = self._loopback() @@ -1404,22 +2006,24 @@ class ConnectionSendTests(TestCase, _LoopbackMixin): class ConnectionSendallTests(TestCase, _LoopbackMixin): """ - Tests for L{Connection.sendall}. + Tests for :py:obj:`Connection.sendall`. """ def test_wrong_args(self): """ - When called with arguments other than a single string, - L{Connection.sendall} raises L{TypeError}. + When called with arguments other than a string argument for its first + parameter or with more than two arguments, :py:obj:`Connection.sendall` + raises :py:obj:`TypeError`. """ connection = Connection(Context(TLSv1_METHOD), None) self.assertRaises(TypeError, connection.sendall) self.assertRaises(TypeError, connection.sendall, object()) - self.assertRaises(TypeError, connection.sendall, "foo", "bar") + self.assertRaises( + TypeError, connection.sendall, "foo", object(), "bar") def test_short(self): """ - L{Connection.sendall} transmits all of the bytes in the string passed to + :py:obj:`Connection.sendall` transmits all of the bytes in the string passed to it. """ server, client = self._loopback() @@ -1435,7 +2039,7 @@ class ConnectionSendallTests(TestCase, _LoopbackMixin): def test_short_memoryview(self): """ When passed a memoryview onto a small number of bytes, - L{Connection.sendall} transmits all of them. + :py:obj:`Connection.sendall` transmits all of them. """ server, client = self._loopback() server.sendall(memoryview(b('x'))) @@ -1444,7 +2048,7 @@ class ConnectionSendallTests(TestCase, _LoopbackMixin): def test_long(self): """ - L{Connection.sendall} transmits all of the bytes in the string passed to + :py:obj:`Connection.sendall` transmits all of the bytes in the string passed to it even if this requires multiple calls of an underlying write function. """ server, client = self._loopback() @@ -1464,12 +2068,16 @@ class ConnectionSendallTests(TestCase, _LoopbackMixin): def test_closed(self): """ - If the underlying socket is closed, L{Connection.sendall} propagates the + If the underlying socket is closed, :py:obj:`Connection.sendall` propagates the write error from the low level write call. """ server, client = self._loopback() server.sock_shutdown(2) - self.assertRaises(SysCallError, server.sendall, "hello, world") + exc = self.assertRaises(SysCallError, server.sendall, b"hello, world") + if platform == "win32": + self.assertEqual(exc.args[0], ESHUTDOWN) + else: + self.assertEqual(exc.args[0], EPIPE) @@ -1479,7 +2087,7 @@ class ConnectionRenegotiateTests(TestCase, _LoopbackMixin): """ def test_renegotiate_wrong_args(self): """ - L{Connection.renegotiate} raises L{TypeError} if called with any + :py:obj:`Connection.renegotiate` raises :py:obj:`TypeError` if called with any arguments. """ connection = Connection(Context(TLSv1_METHOD), None) @@ -1488,7 +2096,7 @@ class ConnectionRenegotiateTests(TestCase, _LoopbackMixin): def test_total_renegotiations_wrong_args(self): """ - L{Connection.total_renegotiations} raises L{TypeError} if called with + :py:obj:`Connection.total_renegotiations` raises :py:obj:`TypeError` if called with any arguments. """ connection = Connection(Context(TLSv1_METHOD), None) @@ -1497,7 +2105,7 @@ class ConnectionRenegotiateTests(TestCase, _LoopbackMixin): def test_total_renegotiations(self): """ - L{Connection.total_renegotiations} returns C{0} before any + :py:obj:`Connection.total_renegotiations` returns :py:obj:`0` before any renegotiations have happened. """ connection = Connection(Context(TLSv1_METHOD), None) @@ -1528,11 +2136,11 @@ class ConnectionRenegotiateTests(TestCase, _LoopbackMixin): class ErrorTests(TestCase): """ - Unit tests for L{OpenSSL.SSL.Error}. + Unit tests for :py:obj:`OpenSSL.SSL.Error`. """ def test_type(self): """ - L{Error} is an exception type. + :py:obj:`Error` is an exception type. """ self.assertTrue(issubclass(Error, Exception)) self.assertEqual(Error.__name__, 'Error') @@ -1541,7 +2149,7 @@ class ErrorTests(TestCase): class ConstantsTests(TestCase): """ - Tests for the values of constants exposed in L{OpenSSL.SSL}. + Tests for the values of constants exposed in :py:obj:`OpenSSL.SSL`. These are values defined by OpenSSL intended only to be used as flags to OpenSSL APIs. The only assertions it seems can be made about them is @@ -1551,8 +2159,8 @@ class ConstantsTests(TestCase): if OP_NO_QUERY_MTU is not None: def test_op_no_query_mtu(self): """ - The value of L{OpenSSL.SSL.OP_NO_QUERY_MTU} is 0x1000, the value of - I{SSL_OP_NO_QUERY_MTU} defined by I{openssl/ssl.h}. + The value of :py:obj:`OpenSSL.SSL.OP_NO_QUERY_MTU` is 0x1000, the value of + :py:const:`SSL_OP_NO_QUERY_MTU` defined by :file:`openssl/ssl.h`. """ self.assertEqual(OP_NO_QUERY_MTU, 0x1000) else: @@ -1562,8 +2170,8 @@ class ConstantsTests(TestCase): if OP_COOKIE_EXCHANGE is not None: def test_op_cookie_exchange(self): """ - The value of L{OpenSSL.SSL.OP_COOKIE_EXCHANGE} is 0x2000, the value - of I{SSL_OP_COOKIE_EXCHANGE} defined by I{openssl/ssl.h}. + The value of :py:obj:`OpenSSL.SSL.OP_COOKIE_EXCHANGE` is 0x2000, the value + of :py:const:`SSL_OP_COOKIE_EXCHANGE` defined by :file:`openssl/ssl.h`. """ self.assertEqual(OP_COOKIE_EXCHANGE, 0x2000) else: @@ -1573,23 +2181,102 @@ class ConstantsTests(TestCase): if OP_NO_TICKET is not None: def test_op_no_ticket(self): """ - The value of L{OpenSSL.SSL.OP_NO_TICKET} is 0x4000, the value of - I{SSL_OP_NO_TICKET} defined by I{openssl/ssl.h}. + The value of :py:obj:`OpenSSL.SSL.OP_NO_TICKET` is 0x4000, the value of + :py:const:`SSL_OP_NO_TICKET` defined by :file:`openssl/ssl.h`. """ self.assertEqual(OP_NO_TICKET, 0x4000) else: "OP_NO_TICKET unavailable - OpenSSL version may be too old" + if OP_NO_COMPRESSION is not None: + def test_op_no_compression(self): + """ + The value of :py:obj:`OpenSSL.SSL.OP_NO_COMPRESSION` is 0x20000, the value + of :py:const:`SSL_OP_NO_COMPRESSION` defined by :file:`openssl/ssl.h`. + """ + self.assertEqual(OP_NO_COMPRESSION, 0x20000) + else: + "OP_NO_COMPRESSION unavailable - OpenSSL version may be too old" + + + def test_sess_cache_off(self): + """ + The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_OFF` 0x0, the value of + :py:obj:`SSL_SESS_CACHE_OFF` defined by ``openssl/ssl.h``. + """ + self.assertEqual(0x0, SESS_CACHE_OFF) + + + def test_sess_cache_client(self): + """ + The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_CLIENT` 0x1, the value of + :py:obj:`SSL_SESS_CACHE_CLIENT` defined by ``openssl/ssl.h``. + """ + self.assertEqual(0x1, SESS_CACHE_CLIENT) + + + def test_sess_cache_server(self): + """ + The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_SERVER` 0x2, the value of + :py:obj:`SSL_SESS_CACHE_SERVER` defined by ``openssl/ssl.h``. + """ + self.assertEqual(0x2, SESS_CACHE_SERVER) + + + def test_sess_cache_both(self): + """ + The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_BOTH` 0x3, the value of + :py:obj:`SSL_SESS_CACHE_BOTH` defined by ``openssl/ssl.h``. + """ + self.assertEqual(0x3, SESS_CACHE_BOTH) + + + def test_sess_cache_no_auto_clear(self): + """ + The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_NO_AUTO_CLEAR` 0x80, the + value of :py:obj:`SSL_SESS_CACHE_NO_AUTO_CLEAR` defined by + ``openssl/ssl.h``. + """ + self.assertEqual(0x80, SESS_CACHE_NO_AUTO_CLEAR) + + + def test_sess_cache_no_internal_lookup(self): + """ + The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_NO_INTERNAL_LOOKUP` 0x100, + the value of :py:obj:`SSL_SESS_CACHE_NO_INTERNAL_LOOKUP` defined by + ``openssl/ssl.h``. + """ + self.assertEqual(0x100, SESS_CACHE_NO_INTERNAL_LOOKUP) + + + def test_sess_cache_no_internal_store(self): + """ + The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_NO_INTERNAL_STORE` 0x200, + the value of :py:obj:`SSL_SESS_CACHE_NO_INTERNAL_STORE` defined by + ``openssl/ssl.h``. + """ + self.assertEqual(0x200, SESS_CACHE_NO_INTERNAL_STORE) + + + def test_sess_cache_no_internal(self): + """ + The value of :py:obj:`OpenSSL.SSL.SESS_CACHE_NO_INTERNAL` 0x300, the + value of :py:obj:`SSL_SESS_CACHE_NO_INTERNAL` defined by + ``openssl/ssl.h``. + """ + self.assertEqual(0x300, SESS_CACHE_NO_INTERNAL) + + class MemoryBIOTests(TestCase, _LoopbackMixin): """ - Tests for L{OpenSSL.SSL.Connection} using a memory BIO. + Tests for :py:obj:`OpenSSL.SSL.Connection` using a memory BIO. """ def _server(self, sock): """ - Create a new server-side SSL L{Connection} object wrapped around - C{sock}. + Create a new server-side SSL :py:obj:`Connection` object wrapped around + :py:obj:`sock`. """ # Create the server side Connection. This is mostly setup boilerplate # - use TLSv1, use a particular certificate, etc. @@ -1610,8 +2297,8 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def _client(self, sock): """ - Create a new client-side SSL L{Connection} object wrapped around - C{sock}. + Create a new client-side SSL :py:obj:`Connection` object wrapped around + :py:obj:`sock`. """ # Now create the client side Connection. Similar boilerplate to the # above. @@ -1630,7 +2317,7 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_memoryConnect(self): """ - Two L{Connection}s which use memory BIOs can be manually connected by + Two :py:obj:`Connection`s which use memory BIOs can be manually connected by reading from the output of each and writing those bytes to the input of the other and in this way establish a connection and exchange application-level bytes with each other. @@ -1674,10 +2361,10 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_socketConnect(self): """ - Just like L{test_memoryConnect} but with an actual socket. + Just like :py:obj:`test_memoryConnect` but with an actual socket. This is primarily to rule out the memory BIO code as the source of - any problems encountered while passing data over a L{Connection} (if + any problems encountered while passing data over a :py:obj:`Connection` (if this test fails, there must be a problem outside the memory BIO code, as no memory BIO is involved here). Even though this isn't a memory BIO test, it's convenient to have it here. @@ -1698,8 +2385,8 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_socketOverridesMemory(self): """ - Test that L{OpenSSL.SSL.bio_read} and L{OpenSSL.SSL.bio_write} don't - work on L{OpenSSL.SSL.Connection}() that use sockets. + Test that :py:obj:`OpenSSL.SSL.bio_read` and :py:obj:`OpenSSL.SSL.bio_write` don't + work on :py:obj:`OpenSSL.SSL.Connection`() that use sockets. """ context = Context(SSLv3_METHOD) client = socket() @@ -1712,7 +2399,7 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_outgoingOverflow(self): """ If more bytes than can be written to the memory BIO are passed to - L{Connection.send} at once, the number of bytes which were written is + :py:obj:`Connection.send` at once, the number of bytes which were written is returned and that many bytes from the beginning of the input can be read from the other end of the connection. """ @@ -1722,7 +2409,7 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): self._interactInMemory(client, server) size = 2 ** 15 - sent = client.send("x" * size) + sent = client.send(b"x" * size) # Sanity check. We're trying to test what happens when the entire # input can't be sent. If the entire input was sent, this test is # meaningless. @@ -1738,8 +2425,8 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_shutdown(self): """ - L{Connection.bio_shutdown} signals the end of the data stream from - which the L{Connection} reads. + :py:obj:`Connection.bio_shutdown` signals the end of the data stream from + which the :py:obj:`Connection` reads. """ server = self._server(None) server.bio_shutdown() @@ -1749,15 +2436,27 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): self.assertEquals(e.__class__, Error) + def test_unexpectedEndOfFile(self): + """ + If the connection is lost before an orderly SSL shutdown occurs, + :py:obj:`OpenSSL.SSL.SysCallError` is raised with a message of + "Unexpected EOF". + """ + server_conn, client_conn = self._loopback() + client_conn.sock_shutdown(SHUT_RDWR) + exc = self.assertRaises(SysCallError, server_conn.recv, 1024) + self.assertEqual(exc.args, (-1, "Unexpected EOF")) + + def _check_client_ca_list(self, func): """ - Verify the return value of the C{get_client_ca_list} method for server and client connections. + Verify the return value of the :py:obj:`get_client_ca_list` method for server and client connections. - @param func: A function which will be called with the server context + :param func: A function which will be called with the server context before the client and server are connected to each other. This function should specify a list of CAs for the server to send to the client and return that same list. The list will be used to verify - that C{get_client_ca_list} returns the proper value at various + that :py:obj:`get_client_ca_list` returns the proper value at various times. """ server = self._server(None) @@ -1775,7 +2474,7 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_set_client_ca_list_errors(self): """ - L{Context.set_client_ca_list} raises a L{TypeError} if called with a + :py:obj:`Context.set_client_ca_list` raises a :py:obj:`TypeError` if called with a non-list or a list that contains objects other than X509Names. """ ctx = Context(TLSv1_METHOD) @@ -1786,9 +2485,9 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_set_empty_ca_list(self): """ - If passed an empty list, L{Context.set_client_ca_list} configures the + If passed an empty list, :py:obj:`Context.set_client_ca_list` configures the context to send no CA names to the client and, on both the server and - client sides, L{Connection.get_client_ca_list} returns an empty list + client sides, :py:obj:`Connection.get_client_ca_list` returns an empty list after the connection is set up. """ def no_ca(ctx): @@ -1800,9 +2499,9 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_set_one_ca_list(self): """ If passed a list containing a single X509Name, - L{Context.set_client_ca_list} configures the context to send that CA + :py:obj:`Context.set_client_ca_list` configures the context to send that CA name to the client and, on both the server and client sides, - L{Connection.get_client_ca_list} returns a list containing that + :py:obj:`Connection.get_client_ca_list` returns a list containing that X509Name after the connection is set up. """ cacert = load_certificate(FILETYPE_PEM, root_cert_pem) @@ -1816,9 +2515,9 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_set_multiple_ca_list(self): """ If passed a list containing multiple X509Name objects, - L{Context.set_client_ca_list} configures the context to send those CA + :py:obj:`Context.set_client_ca_list` configures the context to send those CA names to the client and, on both the server and client sides, - L{Connection.get_client_ca_list} returns a list containing those + :py:obj:`Connection.get_client_ca_list` returns a list containing those X509Names after the connection is set up. """ secert = load_certificate(FILETYPE_PEM, server_cert_pem) @@ -1837,7 +2536,7 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_reset_ca_list(self): """ If called multiple times, only the X509Names passed to the final call - of L{Context.set_client_ca_list} are used to configure the CA names + of :py:obj:`Context.set_client_ca_list` are used to configure the CA names sent to the client. """ cacert = load_certificate(FILETYPE_PEM, root_cert_pem) @@ -1857,7 +2556,7 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_mutated_ca_list(self): """ - If the list passed to L{Context.set_client_ca_list} is mutated + If the list passed to :py:obj:`Context.set_client_ca_list` is mutated afterwards, this does not affect the list of CA names sent to the client. """ @@ -1877,7 +2576,7 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_add_client_ca_errors(self): """ - L{Context.add_client_ca} raises L{TypeError} if called with a non-X509 + :py:obj:`Context.add_client_ca` raises :py:obj:`TypeError` if called with a non-X509 object or with a number of arguments other than one. """ ctx = Context(TLSv1_METHOD) @@ -1890,7 +2589,7 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_one_add_client_ca(self): """ A certificate's subject can be added as a CA to be sent to the client - with L{Context.add_client_ca}. + with :py:obj:`Context.add_client_ca`. """ cacert = load_certificate(FILETYPE_PEM, root_cert_pem) cadesc = cacert.get_subject() @@ -1903,7 +2602,7 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_multiple_add_client_ca(self): """ Multiple CA names can be sent to the client by calling - L{Context.add_client_ca} with multiple X509 objects. + :py:obj:`Context.add_client_ca` with multiple X509 objects. """ cacert = load_certificate(FILETYPE_PEM, root_cert_pem) secert = load_certificate(FILETYPE_PEM, server_cert_pem) @@ -1920,8 +2619,8 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_set_and_add_client_ca(self): """ - A call to L{Context.set_client_ca_list} followed by a call to - L{Context.add_client_ca} results in using the CA names from the first + A call to :py:obj:`Context.set_client_ca_list` followed by a call to + :py:obj:`Context.add_client_ca` results in using the CA names from the first call and the CA name from the second call. """ cacert = load_certificate(FILETYPE_PEM, root_cert_pem) @@ -1941,8 +2640,8 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): def test_set_after_add_client_ca(self): """ - A call to L{Context.set_client_ca_list} after a call to - L{Context.add_client_ca} replaces the CA name specified by the former + A call to :py:obj:`Context.set_client_ca_list` after a call to + :py:obj:`Context.add_client_ca` replaces the CA name specified by the former call with the names specified by the latter cal. """ cacert = load_certificate(FILETYPE_PEM, root_cert_pem) @@ -1960,6 +2659,56 @@ class MemoryBIOTests(TestCase, _LoopbackMixin): self._check_client_ca_list(set_replaces_add_ca) + +class ConnectionBIOTests(TestCase): + """ + Tests for :py:obj:`Connection.bio_read` and :py:obj:`Connection.bio_write`. + """ + def test_wantReadError(self): + """ + :py:obj:`Connection.bio_read` raises :py:obj:`OpenSSL.SSL.WantReadError` + if there are no bytes available to be read from the BIO. + """ + ctx = Context(TLSv1_METHOD) + conn = Connection(ctx, None) + self.assertRaises(WantReadError, conn.bio_read, 1024) + + + def test_buffer_size(self): + """ + :py:obj:`Connection.bio_read` accepts an integer giving the maximum + number of bytes to read and return. + """ + ctx = Context(TLSv1_METHOD) + conn = Connection(ctx, None) + conn.set_connect_state() + try: + conn.do_handshake() + except WantReadError: + pass + data = conn.bio_read(2) + self.assertEqual(2, len(data)) + + + if not PY3: + def test_buffer_size_long(self): + """ + On Python 2 :py:obj:`Connection.bio_read` accepts values of type + :py:obj:`long` as well as :py:obj:`int`. + """ + ctx = Context(TLSv1_METHOD) + conn = Connection(ctx, None) + conn.set_connect_state() + try: + conn.do_handshake() + except WantReadError: + pass + data = conn.bio_read(long(2)) + self.assertEqual(2, len(data)) + + + + class InfoConstantTests(TestCase): """ Tests for assorted constants exposed for use in info callbacks. diff --git a/OpenSSL/test/util.py b/OpenSSL/test/util.py index 643fb91..4e4d812 100644 --- a/OpenSSL/test/util.py +++ b/OpenSSL/test/util.py @@ -8,31 +8,151 @@ U{Twisted}. """ import shutil +import traceback import os, os.path from tempfile import mktemp from unittest import TestCase import sys -from OpenSSL.crypto import Error, _exception_from_error_queue +from OpenSSL._util import exception_from_error_queue +from OpenSSL.crypto import Error -if sys.version_info < (3, 0): - def b(s): - return s - bytes = str -else: - def b(s): - return s.encode("charmap") - bytes = bytes +try: + import memdbg +except Exception: + class _memdbg(object): heap = None + memdbg = _memdbg() +from OpenSSL._util import ffi, lib, byte_string as b class TestCase(TestCase): """ - L{TestCase} adds useful testing functionality beyond what is available - from the standard library L{unittest.TestCase}. + :py:class:`TestCase` adds useful testing functionality beyond what is available + from the standard library :py:class:`unittest.TestCase`. """ + def run(self, result): + run = super(TestCase, self).run + if memdbg.heap is None: + return run(result) + + # Run the test as usual + before = set(memdbg.heap) + run(result) + + # Clean up some long-lived allocations so they won't be reported as + # memory leaks. + lib.CRYPTO_cleanup_all_ex_data() + lib.ERR_remove_thread_state(ffi.NULL) + after = set(memdbg.heap) + + if not after - before: + # No leaks, fast succeed + return + + if result.wasSuccessful(): + # If it passed, run it again with memory debugging + before = set(memdbg.heap) + run(result) + + # Clean up some long-lived allocations so they won't be reported as + # memory leaks. + lib.CRYPTO_cleanup_all_ex_data() + lib.ERR_remove_thread_state(ffi.NULL) + + after = set(memdbg.heap) + + self._reportLeaks(after - before, result) + + + def _reportLeaks(self, leaks, result): + def format_leak(p): + stacks = memdbg.heap[p] + # Eventually look at multiple stacks for the realloc() case. For + # now just look at the original allocation location. + (size, python_stack, c_stack) = stacks[0] + + stack = traceback.format_list(python_stack)[:-1] + + # c_stack looks something like this (interesting parts indicated + # with inserted arrows not part of the data): + # + # /home/exarkun/Projects/pyOpenSSL/branches/use-opentls/__pycache__/_cffi__x89095113xb9185b9b.so(+0x12cf) [0x7fe2e20582cf] + # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a] + # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52] + # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e] + # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419] + # /home/exarkun/Projects/cpython/2.7/python() [0x4d6129] + # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e] + # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalCodeEx+0x1043) [0x4d3726] + # /home/exarkun/Projects/cpython/2.7/python() [0x55fd51] + # /home/exarkun/Projects/cpython/2.7/python(PyObject_Call+0x7e) [0x420ee6] + # /home/exarkun/Projects/cpython/2.7/python(PyEval_CallObjectWithKeywords+0x158) [0x4d56ec] + # /home/exarkun/.local/lib/python2.7/site-packages/cffi-0.5-py2.7-linux-x86_64.egg/_cffi_backend.so(+0xe96e) [0x7fe2e38be96e] + # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64_inner+0x1b9) [0x7fe2e36ad819] + # /usr/lib/x86_64-linux-gnu/libffi.so.6(ffi_closure_unix64+0x46) [0x7fe2e36adb7c] + # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(CRYPTO_malloc+0x64) [0x7fe2e1cef784] <------ end interesting + # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(lh_insert+0x16b) [0x7fe2e1d6a24b] . + # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x61c18) [0x7fe2e1cf0c18] . + # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(+0x625ec) [0x7fe2e1cf15ec] . + # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_new_method+0xe6) [0x7fe2e1d524d6] . + # /lib/x86_64-linux-gnu/libcrypto.so.1.0.0(DSA_generate_parameters+0x3a) [0x7fe2e1d5364a] <------ begin interesting + # /home/exarkun/Projects/opentls/trunk/tls/c/__pycache__/_cffi__x305d4698xb539baaa.so(+0x1f397) [0x7fe2df84d397] + # /home/exarkun/Projects/cpython/2.7/python(PyCFunction_Call+0x8b) [0x56265a] + # /home/exarkun/Projects/cpython/2.7/python() [0x4d5f52] + # /home/exarkun/Projects/cpython/2.7/python(PyEval_EvalFrameEx+0x753b) [0x4d0e1e] + # /home/exarkun/Projects/cpython/2.7/python() [0x4d6419] + # ... + # + # Notice the stack is upside down compared to a Python traceback. + # Identify the start and end of interesting bits and stuff it into the stack we report. + + saved = list(c_stack) + + # Figure the first interesting frame will be after a the cffi-compiled module + while c_stack and '/__pycache__/_cffi__' not in c_stack[-1]: + c_stack.pop() + + # Figure the last interesting frame will always be CRYPTO_malloc, + # since that's where we hooked in to things. + while c_stack and 'CRYPTO_malloc' not in c_stack[0] and 'CRYPTO_realloc' not in c_stack[0]: + c_stack.pop(0) + + if c_stack: + c_stack.reverse() + else: + c_stack = saved[::-1] + stack.extend([frame + "\n" for frame in c_stack]) + + stack.insert(0, "Leaked (%s) at:\n") + return "".join(stack) + + if leaks: + unique_leaks = {} + for p in leaks: + size = memdbg.heap[p][-1][0] + new_leak = format_leak(p) + if new_leak not in unique_leaks: + unique_leaks[new_leak] = [(size, p)] + else: + unique_leaks[new_leak].append((size, p)) + memdbg.free(p) + + for (stack, allocs) in unique_leaks.iteritems(): + allocs_accum = [] + for (size, pointer) in allocs: + + addr = int(ffi.cast('uintptr_t', pointer)) + allocs_accum.append("%d@0x%x" % (size, addr)) + allocs_report = ", ".join(sorted(allocs_accum)) + + result.addError( + self, + (None, Exception(stack % (allocs_report,)), None)) + + def tearDown(self): """ - Clean up any files or directories created using L{TestCase.mktemp}. + Clean up any files or directories created using :py:meth:`TestCase.mktemp`. Subclasses must invoke this method if they override it or the cleanup will not occur. """ @@ -43,21 +163,45 @@ class TestCase(TestCase): elif os.path.exists(temp): os.unlink(temp) try: - _exception_from_error_queue() + exception_from_error_queue(Error) except Error: e = sys.exc_info()[1] if e.args != ([],): self.fail("Left over errors in OpenSSL error queue: " + repr(e)) + def assertIsInstance(self, instance, classOrTuple, message=None): + """ + Fail if C{instance} is not an instance of the given class or of + one of the given classes. + + @param instance: the object to test the type (first argument of the + C{isinstance} call). + @type instance: any. + @param classOrTuple: the class or classes to test against (second + argument of the C{isinstance} call). + @type classOrTuple: class, type, or tuple. + + @param message: Custom text to include in the exception text if the + assertion fails. + """ + if not isinstance(instance, classOrTuple): + if message is None: + suffix = "" + else: + suffix = ": " + message + self.fail("%r is not an instance of %s%s" % ( + instance, classOrTuple, suffix)) + + def failUnlessIn(self, containee, container, msg=None): """ - Fail the test if C{containee} is not found in C{container}. + Fail the test if :py:data:`containee` is not found in :py:data:`container`. - @param containee: the value that should be in C{container} - @param container: a sequence type, or in the case of a mapping type, + :param containee: the value that should be in :py:class:`container` + :param container: a sequence type, or in the case of a mapping type, will follow semantics of 'if key in dict.keys()' - @param msg: if msg is None, then the failure message will be + :param msg: if msg is None, then the failure message will be '%r not in %r' % (first, second) """ if containee not in container: @@ -68,11 +212,11 @@ class TestCase(TestCase): def failUnlessIdentical(self, first, second, msg=None): """ - Fail the test if C{first} is not C{second}. This is an + Fail the test if :py:data:`first` is not :py:data:`second`. This is an obect-identity-equality test, not an object equality - (i.e. C{__eq__}) test. + (i.e. :py:func:`__eq__`) test. - @param msg: if msg is None, then the failure message will be + :param msg: if msg is None, then the failure message will be '%r is not %r' % (first, second) """ if first is not second: @@ -83,11 +227,11 @@ class TestCase(TestCase): def failIfIdentical(self, first, second, msg=None): """ - Fail the test if C{first} is C{second}. This is an + Fail the test if :py:data:`first` is :py:data:`second`. This is an obect-identity-equality test, not an object equality - (i.e. C{__eq__}) test. + (i.e. :py:func:`__eq__`) test. - @param msg: if msg is None, then the failure message will be + :param msg: if msg is None, then the failure message will be '%r is %r' % (first, second) """ if first is second: @@ -98,15 +242,16 @@ class TestCase(TestCase): def failUnlessRaises(self, exception, f, *args, **kwargs): """ - Fail the test unless calling the function C{f} with the given - C{args} and C{kwargs} raises C{exception}. The failure will report - the traceback and call stack of the unexpected exception. + Fail the test unless calling the function :py:data:`f` with the given + :py:data:`args` and :py:data:`kwargs` raises :py:data:`exception`. The + failure will report the traceback and call stack of the unexpected + exception. - @param exception: exception type that is to be expected - @param f: the function to call + :param exception: exception type that is to be expected + :param f: the function to call - @return: The raised exception instance, if it is of the given type. - @raise self.failureException: Raised if the function call does + :return: The raised exception instance, if it is of the given type. + :raise self.failureException: Raised if the function call does not raise an exception or if it raises an exception of a different type. """ @@ -133,31 +278,22 @@ class TestCase(TestCase): """ if self._temporaryFiles is None: self._temporaryFiles = [] - temp = mktemp(dir=".") + temp = b(mktemp(dir=".")) self._temporaryFiles.append(temp) return temp - # Python 2.3 compatibility. - def assertTrue(self, *a, **kw): - return self.failUnless(*a, **kw) - - - def assertFalse(self, *a, **kw): - return self.failIf(*a, **kw) - - # Other stuff def assertConsistentType(self, theType, name, *constructionArgs): """ - Perform various assertions about C{theType} to ensure that it is a + Perform various assertions about :py:data:`theType` to ensure that it is a well-defined type. This is useful for extension types, where it's pretty easy to do something wacky. If something about the type is unusual, an exception will be raised. - @param theType: The type object about which to make assertions. - @param name: A string giving the name of the type. - @param constructionArgs: Positional arguments to use with C{theType} to + :param theType: The type object about which to make assertions. + :param name: A string giving the name of the type. + :param constructionArgs: Positional arguments to use with :py:data:`theType` to create an instance of it. """ self.assertEqual(theType.__name__, name) diff --git a/OpenSSL/util.c b/OpenSSL/util.c deleted file mode 100644 index ca60ccf..0000000 --- a/OpenSSL/util.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * util.c - * - * Copyright (C) AB Strakt - * Copyright (C) Jean-Paul Calderone - * See LICENSE for details. - * - * Utility functions. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - */ -#include -#include "util.h" - -/* - * Flush OpenSSL's error queue and return a list of errors (a (library, - * function, reason) string tuple) - * - * Arguments: None - * Returns: A list of errors (new reference) - */ -PyObject * -error_queue_to_list(void) { - PyObject *errlist, *tuple; - long err; - - errlist = PyList_New(0); - - while ((err = ERR_get_error()) != 0) { - tuple = Py_BuildValue("(sss)", ERR_lib_error_string(err), - ERR_func_error_string(err), - ERR_reason_error_string(err)); - PyList_Append(errlist, tuple); - Py_DECREF(tuple); - } - - return errlist; -} - -void exception_from_error_queue(PyObject *the_Error) { - PyObject *errlist = error_queue_to_list(); - PyErr_SetObject(the_Error, errlist); - Py_DECREF(errlist); -} - -/* - * Flush OpenSSL's error queue and ignore the result - * - * Arguments: None - * Returns: None - */ -void -flush_error_queue(void) { - /* - * Make sure to save the errors to a local. Py_DECREF might expand such - * that it evaluates its argument more than once, which would lead to - * very nasty things if we just invoked it with error_queue_to_list(). - */ - PyObject *list = error_queue_to_list(); - Py_DECREF(list); -} - -#if (PY_VERSION_HEX < 0x02600000) -PyObject* PyOpenSSL_LongToHex(PyObject *o) { - PyObject *hex = NULL; - PyObject *format = NULL; - PyObject *format_args = NULL; - - if ((format_args = Py_BuildValue("(O)", o)) == NULL) { - goto err; - } - - if ((format = PyString_FromString("%x")) == NULL) { - goto err; - } - - if ((hex = PyString_Format(format, format_args)) == NULL) { - goto err; - } - - return hex; - - err: - if (format_args) { - Py_DECREF(format_args); - } - if (format) { - Py_DECREF(format); - } - if (hex) { - Py_DECREF(hex); - } - return NULL; -} -#endif diff --git a/OpenSSL/util.h b/OpenSSL/util.h deleted file mode 100644 index e634b01..0000000 --- a/OpenSSL/util.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * util.h - * - * Copyright (C) AB Strakt - * See LICENSE for details. - * - * Export utility functions and macros. - * See the file RATIONALE for a short explanation of why this module was written. - * - * Reviewed 2001-07-23 - * - */ -#ifndef PyOpenSSL_UTIL_H_ -#define PyOpenSSL_UTIL_H_ - -#include -#include - -/* - * pymemcompat written by Michael Hudson and lets you program to the - * Python 2.3 memory API while keeping backwards compatibility. - */ -#include "pymemcompat.h" - -/* - * py3k defines macros that help with Python 2.x/3.x compatibility. - */ -#include "py3k.h" - - -extern PyObject *error_queue_to_list(void); -extern void exception_from_error_queue(PyObject *the_Error); -extern void flush_error_queue(void); - -/* - * These are needed because there is no "official" way to specify - * WHERE to save the thread state. - */ -#ifdef WITH_THREAD - -/* - * 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_delete_key_value(_pyOpenSSL_tstate_key); \ - 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; } -#endif - -#if !defined(PY_MAJOR_VERSION) || PY_VERSION_HEX < 0x02000000 -static int -PyModule_AddObject(PyObject *m, char *name, PyObject *o) -{ - PyObject *dict; - if (!PyModule_Check(m) || o == NULL) - return -1; - dict = PyModule_GetDict(m); - if (dict == NULL) - return -1; - if (PyDict_SetItemString(dict, name, o)) - return -1; - Py_DECREF(o); - return 0; -} - -static int -PyModule_AddIntConstant(PyObject *m, char *name, long value) -{ - return PyModule_AddObject(m, name, PyInt_FromLong(value)); -} - -static int PyObject_AsFileDescriptor(PyObject *o) -{ - int fd; - PyObject *meth; - - if (PyInt_Check(o)) { - fd = PyInt_AsLong(o); - } - else if (PyLong_Check(o)) { - fd = PyLong_AsLong(o); - } - else if ((meth = PyObject_GetAttrString(o, "fileno")) != NULL) - { - PyObject *fno = PyEval_CallObject(meth, NULL); - Py_DECREF(meth); - if (fno == NULL) - return -1; - - if (PyInt_Check(fno)) { - fd = PyInt_AsLong(fno); - Py_DECREF(fno); - } - else if (PyLong_Check(fno)) { - fd = PyLong_AsLong(fno); - Py_DECREF(fno); - } - else { - PyErr_SetString(PyExc_TypeError, "fileno() returned a non-integer"); - Py_DECREF(fno); - return -1; - } - } - else { - PyErr_SetString(PyExc_TypeError, "argument must be an int, or have a fileno() method."); - return -1; - } - - if (fd < 0) { - PyErr_Format(PyExc_ValueError, "file descriptor cannot be a negative integer (%i)", fd); - return -1; - } - return fd; -} -#endif - -#if !defined(PY_SSIZE_T_MIN) -typedef int Py_ssize_t; -#define PY_SSIZE_T_MAX INT_MAX -#define PY_SSIZE_T_MIN INT_MIN -#endif - -#if (PY_VERSION_HEX < 0x02600000) -extern PyObject* PyOpenSSL_LongToHex(PyObject *o); -#else -#define PyOpenSSL_LongToHex(o) PyNumber_ToBase(o, 16) -#endif - -#ifndef Py_TYPE -#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) -#endif - -#endif diff --git a/OpenSSL/version.py b/OpenSSL/version.py index be51903..307dba0 100644 --- a/OpenSSL/version.py +++ b/OpenSSL/version.py @@ -6,4 +6,4 @@ pyOpenSSL - A simple wrapper around the OpenSSL library """ -__version__ = '0.13' +__version__ = '0.14' diff --git a/PKG-INFO b/PKG-INFO deleted file mode 100644 index 2c881b4..0000000 --- a/PKG-INFO +++ /dev/null @@ -1,15 +0,0 @@ -Metadata-Version: 1.0 -Name: pyOpenSSL -Version: 0.13 -Summary: Python wrapper module around the OpenSSL library -Home-page: http://pyopenssl.sourceforge.net/ -Author: Jean-Paul Calderone -Author-email: exarkun@twistedmatrix.com -License: APL2 -Description: High-level wrapper around a subset of the OpenSSL library, includes - * SSL.Connection objects, wrapping the methods of Python's portable - sockets - * Callbacks written in Python - * Extensive error-handling mechanism, mirroring OpenSSL's error codes - ... and much more ;) -Platform: UNKNOWN diff --git a/README b/README index 191fa5b..1b2a093 100644 --- a/README +++ b/README @@ -4,5 +4,6 @@ pyOpenSSL - A Python wrapper around the OpenSSL library See the file INSTALL for installation instructions. -I appreciate bug reports and patches. Please visit -. +See http://github.com/pyca/pyopenssl for development. + +See https://mail.python.org/mailman/listinfo/pyopenssl-users for the discussion mailing list. diff --git a/dapper/README.Debian b/dapper/README.Debian new file mode 100644 index 0000000..884deb8 --- /dev/null +++ b/dapper/README.Debian @@ -0,0 +1,13 @@ +Debian notes -- pyOpenSSL +------------------------- + +The source package is split up over four binary packages: + +* python2.2-pyopenssl -- compiled for Python 2.2 +* python2.3-pyopenssl -- compiled for Python 2.3 +* python-pyopenssl -- dummy package that depends on the default + Python version (currently 2.3) +* pyopenssl-doc -- documentation for pyOpenSSL + + +Martin Sjögren diff --git a/dapper/changelog b/dapper/changelog new file mode 100644 index 0000000..a9db879 --- /dev/null +++ b/dapper/changelog @@ -0,0 +1,201 @@ +pyopenssl (0.6-2ubuntu3) dapper; urgency=low + + * Drop python2.3 package. + + -- Matthias Klose Tue, 14 Feb 2006 15:32:47 +0000 + +pyopenssl (0.6-2ubuntu2) dapper; urgency=low + + * Rebuild against openssl 0.9.8. + + -- Martin Pitt Mon, 30 Jan 2006 12:54:52 +0000 + +pyopenssl (0.6-2ubuntu1) breezy; urgency=low + + * Synchronize with Debian. + + -- Matthias Klose Wed, 27 Jul 2005 08:52:44 +0000 + +pyopenssl (0.6-2) unstable; urgency=low + + * Add support for python 2.4. (Closes: #297870) + * Build-depend on v0.9.7 of openssl. + + -- Martin Sjogren Mon, 14 Mar 2005 08:56:27 +0100 + +pyopenssl (0.6-1ubuntu3) hoary; urgency=low + + * Drop python2.2 package. + + -- Matthias Klose Mon, 24 Jan 2005 08:17:19 +0100 + +pyopenssl (0.6-1ubuntu2) hoary; urgency=low + + * Tighten build dependency on python. + + -- Matthias Klose Thu, 16 Dec 2004 22:56:53 +0100 + +pyopenssl (0.6-1ubuntu1) hoary; urgency=low + + * Support python2.4 as default python version. + + -- Matthias Klose Wed, 15 Dec 2004 20:59:11 +0100 + +pyopenssl (0.6-1) unstable; urgency=low + + * New upstream release, including: + - Add Netscape SPKI extensions. (Closes: #205132) + - Add X509.subject_name_hash, X509.digest. (Closes: #205136) + - Fix full names of exceptions. (Closes: #250342) + - Add SSL.Context.use_certificate_chain_file. (Closes: #260134) + * Docs are built upstream, so the build-deps have been trimmed. + This also means that HTML and text documentation are back. + * Bumped standards-version. + * Use dh_python. + + -- Martin Sjogren Fri, 13 Aug 2004 20:53:27 +0200 + +pyopenssl (0.5.1-4) unstable; urgency=low + + * Drop HTML and text documentation since latex2html moved to non-free. + This is a temporary solution, until I can hack mkhowto to use something + else. (Closes: #221344) + * Fix the copyright file to mention the copyright holder. + + -- Martin Sjogren Mon, 15 Dec 2003 20:16:25 +0100 + +pyopenssl (0.5.1-3) unstable; urgency=low + + * MANIFEST.in: Include the src/RATIONALE file. (Closes: #197401) + * doc/pyOpenSSL.tex: Fix typo. (Closes: #197435) + * Drop Python 1.5 and 2.1 support. + * Make python-pyopenssl depend on python2.3-pyopenssl, which is no longer + "experimental". + + -- Martin Sjogren Mon, 11 Aug 2003 18:37:07 +0200 + +pyopenssl (0.5.1-2) unstable; urgency=low + + * Make sure names in control and changelog match. Stupid changelogs, bleh. + * Change section to 'python'. + * Rebuild with openssl 0.9.7. (Closes: #189826) + * __init__.py: Import tsafe module. + * tsafe.py: Add some missing methods. + * debian/copyright: Fix Author(s) boilerplate thingy to shut lintian up. + + -- Martin Sjogren Sun, 20 Apr 2003 17:50:24 +0200 + +pyopenssl (0.5.1-1) unstable; urgency=low + + * New upstream version. (Closes: #159530) + * Added a python-pyopenssl dummy package. + * Added an experimental python2.3-pyopenssl package. + + -- Martin Sjögren Sun, 25 Aug 2002 12:08:31 +0200 + +pyopenssl (0.5-1) unstable; urgency=low + + * New upstream version + * Support for python1.5. + * Fix stupid mistakes for python 1.5 and python 2.1. + + -- Martin Sjögren Wed, 24 Jul 2002 09:05:28 +0200 + +pyopenssl (0.4.1-8) unstable; urgency=low + + * Added examples to pyopenssl-doc + + -- Martin Sjögren Wed, 5 Jun 2002 14:58:04 +0200 + +pyopenssl (0.4.1-7) unstable; urgency=low + + * The cute arrow icons in the HTML documentation should be there now too. + + -- Martin Sjögren Thu, 30 May 2002 00:53:44 +0200 + +pyopenssl (0.4.1-6) unstable; urgency=low + + * Commented out some unused things in debian/rules + + -- Martin Sjögren Wed, 29 May 2002 11:20:33 +0200 + +pyopenssl (0.4.1-5) unstable; urgency=low + + * Adding to the build-depends. + * Initial upload (Closes: #140687) + + -- Martin Sjögren Sat, 6 Apr 2002 14:15:49 +0200 + +pyopenssl (0.4.1-4) unstable; urgency=low + + * Fixes in packaging, it shouldn't be regarded a native package now. + + -- Martin Sjögren Sat, 6 Apr 2002 11:26:39 +0200 + +pyopenssl (0.4.1-3) unstable; urgency=low + + * Moved from non-US to main/devel + + -- Martin Sjögren Fri, 5 Apr 2002 22:44:10 +0200 + +pyopenssl (0.4.1-2) unstable; urgency=low + + * Fixes in the packaging, dependencies and build dependencies should be + all right now. + + -- Martin Sjögren Thu, 10 Jan 2002 10:00:06 +0100 + +pyopenssl (0.4.1-1) unstable; urgency=low + + * New "upstream" release + * New packaging, python2.1-pyopenssl, python2.2-pyopenssl, pyopenssl-doc + + -- Martin Sjögren Mon, 7 Jan 2002 15:38:51 +0100 + +pyopenssl (0.4-4) unstable; urgency=low + + * Grrr, this time then... + + -- Martin Sjögren Fri, 17 Aug 2001 14:53:19 +0200 + +pyopenssl (0.4-3) unstable; urgency=low + + * Fixed a big nasty bug + + -- Martin Sjögren Fri, 17 Aug 2001 14:33:06 +0200 + +pyopenssl (0.4-2) unstable; urgency=low + + * Fixes + + -- Martin Sjögren Fri, 17 Aug 2001 13:53:11 +0200 + +pyopenssl (0.4-1) unstable; urgency=low + + * New "upstream" version + + -- Martin Sjögren Thu, 9 Aug 2001 12:32:47 +0200 + +pyopenssl (0.3-3) unstable; urgency=low + + * X509 objects now has a has_expired method + + -- Martin Sjögren Tue, 7 Aug 2001 14:16:13 +0200 + +pyopenssl (0.3-2) unstable; urgency=low + + * X509Name objects now has a compare method + + -- Martin Sjögren Tue, 7 Aug 2001 10:53:58 +0200 + +pyopenssl (0.3-1) unstable; urgency=low + + * New "upstream" version + + -- Martin Sjögren Fri, 3 Aug 2001 16:36:26 +0200 + +pyopenssl (0.1-1) unstable; urgency=low + + * Initial version. + + -- Anders Hammarquist Mon, 23 Jul 2001 15:17:38 +0200 diff --git a/dapper/control b/dapper/control new file mode 100644 index 0000000..5d33a7f --- /dev/null +++ b/dapper/control @@ -0,0 +1,31 @@ +Source: pyopenssl +Section: python +Priority: optional +Maintainer: Martin Sjogren +Build-Depends: debhelper (>> 4.2.28), python (>= 2.4), python2.4-dev, libssl-dev (>= 0.9.7) +Standards-Version: 3.6.1 + +Package: python2.4-pyopenssl +Architecture: any +Depends: ${shlibs:Depends}, ${python:Depends} +Suggests: pyopenssl-doc +Description: Python wrapper around the OpenSSL library, experimental! + Includes: SSL Context objects, SSL Connection objects, using + Python sockets as transport layer. The Connection object + wraps all the socket methods and can therefore be used + interchangeably. + +Package: python-pyopenssl +Architecture: all +Depends: ${python:Depends} +Description: Python wrapper around the OpenSSL library (dummy package) + This is a dummy package that depends on the version of pyOpenSSL + that is compiled for the default version of Python. + +Package: pyopenssl-doc +Section: doc +Architecture: all +Suggests: python-pyopenssl +Description: Documentation for pyOpenSSL + This package provides documentation for the pyOpenSSL package, + in HTML, postscript and text formats. diff --git a/dapper/copyright b/dapper/copyright new file mode 100644 index 0000000..2705399 --- /dev/null +++ b/dapper/copyright @@ -0,0 +1,22 @@ +This package was debianized by Martin Sjögren on +Mon, 7 Jan 2002 16:25:58 +0100. + +It was downloaded from pyopenssl.sourceforge.net + +Upstream Author: Martin Sjögren + +Copyright (C) 2001-2004 AB Strakt + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +A copy of the GNU Lesser General Public License (version 2.1) +can be found in /usr/share/common-licenses/LGPL on Debian systems, +or in the file COPYING in the source package. diff --git a/dapper/pyopenssl-doc.doc-base b/dapper/pyopenssl-doc.doc-base new file mode 100644 index 0000000..e57eb3f --- /dev/null +++ b/dapper/pyopenssl-doc.doc-base @@ -0,0 +1,13 @@ +Document: pyopenssl-manual +Title: Python OpenSSL Manual +Author: Martin Sjögren +Abstract: Manual for the pyOpenSSL package. + This module is a rather thin wrapper around (a subset of) the + OpenSSL library. With thin wrapper I mean that a lot of the + object methods do nothing more than calling a corresponding + function in the OpenSSL library. +Section: Apps/programming + +Format: HTML +Index: /usr/share/doc/pyopenssl-doc/html/index.html +Files: /usr/share/doc/pyopenssl-doc/html/* diff --git a/dapper/pyopenssl-doc.docs b/dapper/pyopenssl-doc.docs new file mode 100644 index 0000000..0149db1 --- /dev/null +++ b/dapper/pyopenssl-doc.docs @@ -0,0 +1,4 @@ +doc/pyOpenSSL.ps +doc/pyOpenSSL.txt +doc/html +debian/README.Debian diff --git a/dapper/pyopenssl-doc.examples b/dapper/pyopenssl-doc.examples new file mode 100644 index 0000000..e39721e --- /dev/null +++ b/dapper/pyopenssl-doc.examples @@ -0,0 +1 @@ +examples/* diff --git a/dapper/python-pyopenssl.docs b/dapper/python-pyopenssl.docs new file mode 100644 index 0000000..e174728 --- /dev/null +++ b/dapper/python-pyopenssl.docs @@ -0,0 +1 @@ +debian/README.Debian diff --git a/dapper/rules b/dapper/rules new file mode 100755 index 0000000..2007485 --- /dev/null +++ b/dapper/rules @@ -0,0 +1,103 @@ +#!/usr/bin/make -f +# Sample debian/rules that uses debhelper. +# GNU copyright 1997 by Joey Hess. +# +# This version is for a hypothetical package that builds an +# architecture-dependant package, as well as an architecture-independent +# package. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# This is the debhelper compatability version to use. +export DH_COMPAT=3 + +VERSIONS = 2.3 2.4 2.5 + +configure: configure-stamp +configure-stamp: + dh_testdir + + for ver in $(VERSIONS); do \ + cp debian/python-pyopenssl.docs debian/python$$ver-pyopenssl.docs; \ + done + + touch configure-stamp + +build: configure-stamp build-stamp +build-stamp: + dh_testdir + + for ver in $(VERSIONS); do \ + /usr/bin/python$$ver setup.py build; \ + done + + touch build-stamp + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + for ver in $(VERSIONS); do \ + rm -f debian/python$$ver-pyopenssl.docs; \ + done + rm -rf build + + dh_clean + +install: DH_OPTIONS= +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + for ver in $(VERSIONS); do \ + /usr/bin/python$$ver setup.py install --prefix=$(CURDIR)/debian/python$$ver-pyopenssl/usr; \ + done + +# Build architecture-independent files here. +# Pass -i to all debhelper commands in this target to reduce clutter. +binary-indep: build install + dh_testdir -i + dh_testroot -i + dh_installdocs -i + dh_installexamples -i +# dh_installmenu -i +# dh_installcron -i +# dh_installinfo -i + dh_installchangelogs ChangeLog -i +# dh_link -i + dh_compress -i + dh_fixperms -i + dh_python -i + dh_installdeb -i + dh_gencontrol -i + dh_md5sums -i + dh_builddeb -i + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir -a + dh_testroot -a + dh_installdocs -a + dh_installexamples -a +# dh_installmenu -a +# dh_installcron -a +# dh_installinfo -a + dh_installchangelogs ChangeLog -a + dh_strip -a +# dh_link -a + dh_compress -a + dh_fixperms -a + dh_python -a + dh_makeshlibs -a + dh_installdeb -a + dh_shlibdeps -a + dh_gencontrol -a + dh_md5sums -a + dh_builddeb -a + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/doc/Makefile b/doc/Makefile index 07aabdc..4202bee 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,20 +1,130 @@ -PAPER = a4 -MKHOWTO = python tools/mkhowto --$(PAPER) +# Makefile for Sphinx documentation +# -default: html +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build -all: ps html text dvi +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -dvi ps text: pyOpenSSL.tex - $(MKHOWTO) --$@ $^ +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest -html: pyOpenSSL.tex - $(MKHOWTO) --html --iconserver . $^ - -rm -rf html - mv pyOpenSSL html +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: - rm -rf html pyOpenSSL.dvi pyOpenSSL.ps pyOpenSSL.txt \ - pyOpenSSL.l2h pyOpenSSL.how + -rm -rf $(BUILDDIR)/* -.PHONY: default all html dvi ps text clean +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyOpenSSL.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyOpenSSL.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/pyOpenSSL" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pyOpenSSL" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + make -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/Quotes b/doc/Quotes new file mode 100644 index 0000000..b2cdb2d --- /dev/null +++ b/doc/Quotes @@ -0,0 +1,2 @@ +< Screwtape> I like how developing against OpenSSL is like a text adventure game with a maze of twisty passages, all alike. +% diff --git a/doc/README b/doc/README new file mode 100644 index 0000000..2a525bb --- /dev/null +++ b/doc/README @@ -0,0 +1,4 @@ +This is the pyOpenSSL documentation source. It uses Sphinx. To build the +documentation, install Sphinx 1.0 and run: + + $ make html diff --git a/doc/api.rst b/doc/api.rst new file mode 100644 index 0000000..826ec4d --- /dev/null +++ b/doc/api.rst @@ -0,0 +1,18 @@ +.. _openssl: + +:py:mod:`OpenSSL` --- Python interface to OpenSSL +================================================= + +.. py:module:: OpenSSL + :synopsis: Python interface to OpenSSL + + +This package provides a high-level interface to the functions in the +OpenSSL library. The following modules are defined: + +.. toctree:: + :maxdepth: 2 + + api/crypto + api/rand + api/ssl diff --git a/doc/api/crypto.rst b/doc/api/crypto.rst new file mode 100644 index 0000000..7c77b03 --- /dev/null +++ b/doc/api/crypto.rst @@ -0,0 +1,753 @@ +.. _openssl-crypto: + +:py:mod:`crypto` --- Generic cryptographic module +================================================= + +.. py:module:: OpenSSL.crypto + :synopsis: Generic cryptographic module + + +.. py:data:: X509Type + + See :py:class:`X509`. + + +.. py:class:: X509() + + A class representing X.509 certificates. + + +.. py:data:: X509NameType + + See :py:class:`X509Name`. + + +.. py:class:: X509Name(x509name) + + A class representing X.509 Distinguished Names. + + This constructor creates a copy of *x509name* which should be an + instance of :py:class:`X509Name`. + + +.. py:data:: X509ReqType + + See :py:class:`X509Req`. + + +.. py:class:: X509Req() + + A class representing X.509 certificate requests. + + +.. py:data:: X509StoreType + + A Python type object representing the X509Store object type. + + +.. py:data:: PKeyType + + See :py:class:`PKey`. + + +.. py:class:: PKey() + + A class representing DSA or RSA keys. + + +.. py:data:: PKCS7Type + + A Python type object representing the PKCS7 object type. + + +.. py:data:: PKCS12Type + + A Python type object representing the PKCS12 object type. + + +.. py:data:: X509ExtensionType + + See :py:class:`X509Extension`. + + +.. py:class:: X509Extension(typename, critical, value[, subject][, issuer]) + + A class representing an X.509 v3 certificate extensions. See + http://openssl.org/docs/apps/x509v3_config.html#STANDARD_EXTENSIONS for + *typename* strings and their options. Optional parameters *subject* and + *issuer* must be X509 objects. + + +.. py:data:: NetscapeSPKIType + + See :py:class:`NetscapeSPKI`. + + +.. py:class:: NetscapeSPKI([enc]) + + A class representing Netscape SPKI objects. + + If the *enc* argument is present, it should be a base64-encoded string + representing a NetscapeSPKI object, as returned by the :py:meth:`b64_encode` + method. + + +.. py:class:: CRL() + + A class representing Certifcate Revocation List objects. + + +.. py:class:: Revoked() + + A class representing Revocation objects of CRL. + + +.. py:data:: FILETYPE_PEM + FILETYPE_ASN1 + + File type constants. + + +.. py:data:: TYPE_RSA + TYPE_DSA + + Key type constants. + + +.. py:exception:: Error + + Generic exception used in the :py:mod:`.crypto` module. + + +.. py:function:: dump_certificate(type, cert) + + Dump the certificate *cert* into a buffer string encoded with the type + *type*. + + +.. py:function:: dump_certificate_request(type, req) + + Dump the certificate request *req* into a buffer string encoded with the + type *type*. + + +.. py:function:: dump_privatekey(type, pkey[, cipher, passphrase]) + + Dump the private key *pkey* into a buffer string encoded with the type + *type*, optionally (if *type* is :py:const:`FILETYPE_PEM`) encrypting it + using *cipher* and *passphrase*. + + *passphrase* must be either a string or a callback for providing the + pass phrase. + + +.. py:function:: load_certificate(type, buffer) + + Load a certificate (X509) from the string *buffer* encoded with the + type *type*. + + +.. py:function:: load_certificate_request(type, buffer) + + Load a certificate request (X509Req) from the string *buffer* encoded with + the type *type*. + + +.. py:function:: load_privatekey(type, buffer[, passphrase]) + + Load a private key (PKey) from the string *buffer* encoded with the type + *type* (must be one of :py:const:`FILETYPE_PEM` and + :py:const:`FILETYPE_ASN1`). + + *passphrase* must be either a string or a callback for providing the pass + phrase. + + +.. py:function:: load_crl(type, buffer) + + Load Certificate Revocation List (CRL) data from a string *buffer*. + *buffer* encoded with the type *type*. The type *type* must either + :py:const:`FILETYPE_PEM` or :py:const:`FILETYPE_ASN1`). + + +.. py:function:: load_pkcs7_data(type, buffer) + + Load pkcs7 data from the string *buffer* encoded with the type *type*. + + +.. py:function:: load_pkcs12(buffer[, passphrase]) + + Load pkcs12 data from the string *buffer*. If the pkcs12 structure is + encrypted, a *passphrase* must be included. The MAC is always + checked and thus required. + + See also the man page for the C function :py:func:`PKCS12_parse`. + + +.. py:function:: sign(key, data, digest) + + Sign a data string using the given key and message digest. + + *key* is a :py:class:`PKey` instance. *data* is a ``str`` instance. + *digest* is a ``str`` naming a supported message digest type, for example + :py:const:`sha1`. + + .. versionadded:: 0.11 + + +.. py:function:: verify(certificate, signature, data, digest) + + Verify the signature for a data string. + + *certificate* is a :py:class:`X509` instance corresponding to the private + key which generated the signature. *signature* is a *str* instance giving + the signature itself. *data* is a *str* instance giving the data to which + the signature applies. *digest* is a *str* instance naming the message + digest type of the signature, for example :py:const:`sha1`. + + .. versionadded:: 0.11 + + +.. _openssl-x509: + +X509 objects +------------ + +X509 objects have the following methods: + +.. py:method:: X509.get_issuer() + + Return an X509Name object representing the issuer of the certificate. + + +.. py:method:: X509.get_pubkey() + + Return a :py:class:`PKey` object representing the public key of the certificate. + + +.. py:method:: X509.get_serial_number() + + Return the certificate serial number. + + +.. py:method:: X509.get_signature_algorithm() + + Return the signature algorithm used in the certificate. If the algorithm is + undefined, raise :py:data:`ValueError`. + + ..versionadded:: 0.13 + + +.. py:method:: X509.get_subject() + + Return an :py:class:`X509Name` object representing the subject of the certificate. + + +.. py:method:: X509.get_version() + + Return the certificate version. + + +.. py:method:: X509.get_notBefore() + + Return a string giving the time before which the certificate is not valid. The + string is formatted as an ASN1 GENERALIZEDTIME:: + + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm + + If no value exists for this field, :py:data:`None` is returned. + + +.. py:method:: X509.get_notAfter() + + Return a string giving the time after which the certificate is not valid. The + string is formatted as an ASN1 GENERALIZEDTIME:: + + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm + + If no value exists for this field, :py:data:`None` is returned. + + +.. py:method:: X509.set_notBefore(when) + + Change the time before which the certificate is not valid. *when* is a + string formatted as an ASN1 GENERALIZEDTIME:: + + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm + + +.. py:method:: X509.set_notAfter(when) + + Change the time after which the certificate is not valid. *when* is a + string formatted as an ASN1 GENERALIZEDTIME:: + + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm + + + +.. py:method:: X509.gmtime_adj_notBefore(time) + + Adjust the timestamp (in GMT) when the certificate starts being valid. + + +.. py:method:: X509.gmtime_adj_notAfter(time) + + Adjust the timestamp (in GMT) when the certificate stops being valid. + + +.. py:method:: X509.has_expired() + + Checks the certificate's time stamp against current time. Returns true if the + certificate has expired and false otherwise. + + +.. py:method:: X509.set_issuer(issuer) + + Set the issuer of the certificate to *issuer*. + + +.. py:method:: X509.set_pubkey(pkey) + + Set the public key of the certificate to *pkey*. + + +.. py:method:: X509.set_serial_number(serialno) + + Set the serial number of the certificate to *serialno*. + + +.. py:method:: X509.set_subject(subject) + + Set the subject of the certificate to *subject*. + + +.. py:method:: X509.set_version(version) + + Set the certificate version to *version*. + + +.. py:method:: X509.sign(pkey, digest) + + Sign the certificate, using the key *pkey* and the message digest algorithm + identified by the string *digest*. + + +.. py:method:: X509.subject_name_hash() + + Return the hash of the certificate subject. + +.. py:method:: X509.digest(digest_name) + + Return a digest of the certificate, using the *digest_name* method. + *digest_name* must be a string describing a digest algorithm supported + by OpenSSL (by EVP_get_digestbyname, specifically). For example, + :py:const:`"md5"` or :py:const:`"sha1"`. + + +.. py:method:: X509.add_extensions(extensions) + + Add the extensions in the sequence *extensions* to the certificate. + + +.. py:method:: X509.get_extension_count() + + Return the number of extensions on this certificate. + + .. versionadded:: 0.12 + + +.. py:method:: X509.get_extension(index) + + Retrieve the extension on this certificate at the given index. + + Extensions on a certificate are kept in order. The index parameter selects + which extension will be returned. The returned object will be an + :py:class:`X509Extension` instance. + + .. versionadded:: 0.12 + + +.. _openssl-x509name: + +X509Name objects +---------------- + +X509Name objects have the following methods: + +.. py:method:: X509Name.hash() + + Return an integer giving the first four bytes of the MD5 digest of the DER + representation of the name. + + +.. py:method:: X509Name.der() + + Return a string giving the DER representation of the name. + + +.. py:method:: X509Name.get_components() + + Return a list of two-tuples of strings giving the components of the name. + + +X509Name objects have the following members: + +.. py:attribute:: X509Name.countryName + + The country of the entity. :py:attr:`C` may be used as an alias for + :py:attr:`countryName`. + + +.. py:attribute:: X509Name.stateOrProvinceName + + The state or province of the entity. :py:attr:`ST` may be used as an alias for + :py:attr:`stateOrProvinceName`. + + +.. py:attribute:: X509Name.localityName + + The locality of the entity. :py:attr:`L` may be used as an alias for + :py:attr:`localityName`. + + +.. py:attribute:: X509Name.organizationName + + The organization name of the entity. :py:attr:`O` may be used as an alias for + :py:attr:`organizationName`. + + +.. py:attribute:: X509Name.organizationalUnitName + + The organizational unit of the entity. :py:attr:`OU` may be used as an alias for + :py:attr:`organizationalUnitName`. + + +.. py:attribute:: X509Name.commonName + + The common name of the entity. :py:attr:`CN` may be used as an alias for + :py:attr:`commonName`. + + +.. py:attribute:: X509Name.emailAddress + + The e-mail address of the entity. + + +.. _openssl-x509req: + +X509Req objects +--------------- + +X509Req objects have the following methods: + +.. py:method:: X509Req.get_pubkey() + + Return a :py:class:`PKey` object representing the public key of the certificate request. + + +.. py:method:: X509Req.get_subject() + + Return an :py:class:`X509Name` object representing the subject of the certificate. + + +.. py:method:: X509Req.set_pubkey(pkey) + + Set the public key of the certificate request to *pkey*. + + +.. py:method:: X509Req.sign(pkey, digest) + + Sign the certificate request, using the key *pkey* and the message digest + algorithm identified by the string *digest*. + + +.. py:method:: X509Req.verify(pkey) + + Verify a certificate request using the public key *pkey*. + + +.. py:method:: X509Req.set_version(version) + + Set the version (RFC 2459, 4.1.2.1) of the certificate request to + *version*. + + +.. py:method:: X509Req.get_version() + + Get the version (RFC 2459, 4.1.2.1) of the certificate request. + + +.. _openssl-x509store: + +X509Store objects +----------------- + +The X509Store object has currently just one method: + +.. py:method:: X509Store.add_cert(cert) + + Add the certificate *cert* to the certificate store. + + +.. _openssl-pkey: + +PKey objects +------------ + +The PKey object has the following methods: + +.. py:method:: PKey.bits() + + Return the number of bits of the key. + + +.. py:method:: PKey.generate_key(type, bits) + + Generate a public/private key pair of the type *type* (one of + :py:const:`TYPE_RSA` and :py:const:`TYPE_DSA`) with the size *bits*. + + +.. py:method:: PKey.type() + + Return the type of the key. + + +.. py:method:: PKey.check() + + Check the consistency of this key, returning True if it is consistent and + raising an exception otherwise. This is only valid for RSA keys. See the + OpenSSL RSA_check_key man page for further limitations. + + +.. _openssl-pkcs7: + +PKCS7 objects +------------- + +PKCS7 objects have the following methods: + +.. py:method:: PKCS7.type_is_signed() + + FIXME + + +.. py:method:: PKCS7.type_is_enveloped() + + FIXME + + +.. py:method:: PKCS7.type_is_signedAndEnveloped() + + FIXME + + +.. py:method:: PKCS7.type_is_data() + + FIXME + + +.. py:method:: PKCS7.get_type_name() + + Get the type name of the PKCS7. + + +.. _openssl-pkcs12: + +PKCS12 objects +-------------- + +PKCS12 objects have the following methods: + +.. py:method:: PKCS12.export([passphrase=None][, iter=2048][, maciter=1]) + + Returns a PKCS12 object as a string. + + The optional *passphrase* must be a string not a callback. + + See also the man page for the C function :py:func:`PKCS12_create`. + + +.. py:method:: PKCS12.get_ca_certificates() + + Return CA certificates within the PKCS12 object as a tuple. Returns + :py:const:`None` if no CA certificates are present. + + +.. py:method:: PKCS12.get_certificate() + + Return certificate portion of the PKCS12 structure. + + +.. py:method:: PKCS12.get_friendlyname() + + Return friendlyName portion of the PKCS12 structure. + + +.. py:method:: PKCS12.get_privatekey() + + Return private key portion of the PKCS12 structure + + +.. py:method:: PKCS12.set_ca_certificates(cacerts) + + Replace or set the CA certificates within the PKCS12 object with the sequence *cacerts*. + + Set *cacerts* to :py:const:`None` to remove all CA certificates. + + +.. py:method:: PKCS12.set_certificate(cert) + + Replace or set the certificate portion of the PKCS12 structure. + + +.. py:method:: PKCS12.set_friendlyname(name) + + Replace or set the friendlyName portion of the PKCS12 structure. + + +.. py:method:: PKCS12.set_privatekey(pkey) + + Replace or set private key portion of the PKCS12 structure + + +.. _openssl-509ext: + +X509Extension objects +--------------------- + +X509Extension objects have several methods: + +.. py:method:: X509Extension.get_critical() + + Return the critical field of the extension object. + + +.. py:method:: X509Extension.get_short_name() + + Retrieve the short descriptive name for this extension. + + The result is a byte string like :py:const:`basicConstraints`. + + .. versionadded:: 0.12 + + +.. py:method:: X509Extension.get_data() + + Retrieve the data for this extension. + + The result is the ASN.1 encoded form of the extension data as a byte string. + + .. versionadded:: 0.12 + + +.. _openssl-netscape-spki: + +NetscapeSPKI objects +-------------------- + +NetscapeSPKI objects have the following methods: + +.. py:method:: NetscapeSPKI.b64_encode() + + Return a base64-encoded string representation of the object. + + +.. py:method:: NetscapeSPKI.get_pubkey() + + Return the public key of object. + + +.. py:method:: NetscapeSPKI.set_pubkey(key) + + Set the public key of the object to *key*. + + +.. py:method:: NetscapeSPKI.sign(key, digest_name) + + Sign the NetscapeSPKI object using the given *key* and *digest_name*. + *digest_name* must be a string describing a digest algorithm supported by + OpenSSL (by EVP_get_digestbyname, specifically). For example, + :py:const:`"md5"` or :py:const:`"sha1"`. + + +.. py:method:: NetscapeSPKI.verify(key) + + Verify the NetscapeSPKI object using the given *key*. + + +.. _crl: + +CRL objects +----------- + +CRL objects have the following methods: + +.. py:method:: CRL.add_revoked(revoked) + + Add a Revoked object to the CRL, by value not reference. + + +.. py:method:: CRL.export(cert, key[, type=FILETYPE_PEM][, days=100]) + + Use *cert* and *key* to sign the CRL and return the CRL as a string. + *days* is the number of days before the next CRL is due. + + +.. py:method:: CRL.get_revoked() + + Return a tuple of Revoked objects, by value not reference. + + +.. _revoked: + +Revoked objects +--------------- + +Revoked objects have the following methods: + +.. py:method:: Revoked.all_reasons() + + Return a list of all supported reasons. + + +.. py:method:: Revoked.get_reason() + + Return the revocation reason as a str. Can be + None, which differs from "Unspecified". + + +.. py:method:: Revoked.get_rev_date() + + Return the revocation date as a str. + The string is formatted as an ASN1 GENERALIZEDTIME. + + +.. py:method:: Revoked.get_serial() + + Return a str containing a hex number of the serial of the revoked certificate. + + +.. py:method:: Revoked.set_reason(reason) + + Set the revocation reason. *reason* must be None or a string, but the + values are limited. Spaces and case are ignored. See + :py:meth:`all_reasons`. + + +.. py:method:: Revoked.set_rev_date(date) + + Set the revocation date. + The string is formatted as an ASN1 GENERALIZEDTIME. + + +.. py:method:: Revoked.set_serial(serial) + + *serial* is a string containing a hex number of the serial of the revoked certificate. diff --git a/doc/api/rand.rst b/doc/api/rand.rst new file mode 100644 index 0000000..18789b8 --- /dev/null +++ b/doc/api/rand.rst @@ -0,0 +1,79 @@ +.. _openssl-rand: + +:py:mod:`rand` --- An interface to the OpenSSL pseudo random number generator +============================================================================= + +.. py:module:: OpenSSL.rand + :synopsis: An interface to the OpenSSL pseudo random number generator + + +This module handles the OpenSSL pseudo random number generator (PRNG) and +declares the following: + +.. py:function:: add(string, entropy) + + Mix bytes from *string* into the PRNG state. The *entropy* argument is + (the lower bound of) an estimate of how much randomness is contained in + *string*, measured in bytes. For more information, see e.g. :rfc:`1750`. + + +.. py:function:: bytes(num_bytes) + + Get some random bytes from the PRNG as a string. + + This is a wrapper for the C function :py:func:`RAND_bytes`. + + +.. py:function:: cleanup() + + Erase the memory used by the PRNG. + + This is a wrapper for the C function :py:func:`RAND_cleanup`. + + +.. py:function:: egd(path[, bytes]) + + Query the `Entropy Gathering Daemon `_ on + socket *path* for *bytes* bytes of random data and uses :py:func:`add` to + seed the PRNG. The default value of *bytes* is 255. + + +.. py:function:: load_file(path[, bytes]) + + Read *bytes* bytes (or all of it, if *bytes* is negative) of data from the + file *path* to seed the PRNG. The default value of *bytes* is -1. + + +.. py:function:: screen() + + Add the current contents of the screen to the PRNG state. + + Availability: Windows. + + +.. py:function:: seed(string) + + This is equivalent to calling :py:func:`add` with *entropy* as the length + of the string. + + +.. py:function:: status() + + Returns true if the PRNG has been seeded with enough data, and false otherwise. + + +.. py:function:: write_file(path) + + Write a number of random bytes (currently 1024) to the file *path*. This + file can then be used with :py:func:`load_file` to seed the PRNG again. + + +.. py:exception:: Error + + If the current RAND method supports any errors, this is raised when needed. + The default method does not raise this when the entropy pool is depleted. + + Whenever this exception is raised directly, it has a list of error messages + from the OpenSSL error queue, where each item is a tuple *(lib, function, + reason)*. Here *lib*, *function* and *reason* are all strings, describing + where and what the problem is. See :manpage:`err(3)` for more information. diff --git a/doc/api/ssl.rst b/doc/api/ssl.rst new file mode 100644 index 0000000..b506757 --- /dev/null +++ b/doc/api/ssl.rst @@ -0,0 +1,773 @@ +.. _openssl-ssl: + +:py:mod:`SSL` --- An interface to the SSL-specific parts of OpenSSL +=================================================================== + +.. py:module:: OpenSSL.SSL + :synopsis: An interface to the SSL-specific parts of OpenSSL + + +This module handles things specific to SSL. There are two objects defined: +Context, Connection. + +.. py:data:: SSLv2_METHOD + SSLv3_METHOD + SSLv23_METHOD + TLSv1_METHOD + TLSv1_1_METHOD + TLSv1_2_METHOD + + These constants represent the different SSL methods to use when creating a + context object. If the underlying OpenSSL build is missing support for any + of these protocols, constructing a :py:class:`Context` using the + corresponding :py:const:`*_METHOD` will raise an exception. + + +.. py:data:: VERIFY_NONE + VERIFY_PEER + VERIFY_FAIL_IF_NO_PEER_CERT + + These constants represent the verification mode used by the Context + object's :py:meth:`set_verify` method. + + +.. py:data:: FILETYPE_PEM + FILETYPE_ASN1 + + File type constants used with the :py:meth:`use_certificate_file` and + :py:meth:`use_privatekey_file` methods of Context objects. + + +.. py:data:: OP_SINGLE_DH_USE + + Constant used with :py:meth:`set_options` of Context objects. + + When this option is used, a new key will always be created when using + ephemeral Diffie-Hellman. + + +.. py:data:: OP_EPHEMERAL_RSA + + Constant used with :py:meth:`set_options` of Context objects. + + When this option is used, ephemeral RSA keys will always be used when doing + RSA operations. + + +.. py:data:: OP_NO_TICKET + + Constant used with :py:meth:`set_options` of Context objects. + + When this option is used, the session ticket extension will not be used. + + +.. py:data:: OP_NO_COMPRESSION + + Constant used with :py:meth:`set_options` of Context objects. + + When this option is used, compression will not be used. + + +.. py:data:: OP_NO_SSLv2 + OP_NO_SSLv3 + OP_NO_TLSv1 + OP_NO_TLSv1_1 + OP_NO_TLSv1_2 + + Constants used with :py:meth:`set_options` of Context objects. + + Each of these options disables one version of the SSL/TLS protocol. This + is interesting if you're using e.g. :py:const:`SSLv23_METHOD` to get an + SSLv2-compatible handshake, but don't want to use SSLv2. If the underlying + OpenSSL build is missing support for any of these protocols, the + :py:const:`OP_NO_*` constant may be undefined. + + +.. py:data:: MODE_NO_COMPRESSION + + Constant used with :py:meth:`set_mode` of Context objects to disable + automatic compression of application traffic. + + +.. py:data:: SSLEAY_VERSION + SSLEAY_CFLAGS + SSLEAY_BUILT_ON + SSLEAY_PLATFORM + SSLEAY_DIR + + Constants used with :py:meth:`SSLeay_version` to specify what OpenSSL version + information to retrieve. See the man page for the :py:func:`SSLeay_version` C + API for details. + + +.. py:data:: SESS_CACHE_OFF + SESS_CACHE_CLIENT + SESS_CACHE_SERVER + SESS_CACHE_BOTH + SESS_CACHE_NO_AUTO_CLEAR + SESS_CACHE_NO_INTERNAL_LOOKUP + SESS_CACHE_NO_INTERNAL_STORE + SESS_CACHE_NO_INTERNAL + + Constants used with :py:meth:`Context.set_session_cache_mode` to specify + the behavior of the session cache and potential session reuse. See the man + page for the :py:func:`SSL_CTX_set_session_cache_mode` C API for details. + + .. versionadded:: 0.14 + + +.. py:data:: OPENSSL_VERSION_NUMBER + + An integer giving the version number of the OpenSSL library used to build this + version of pyOpenSSL. See the man page for the :py:func:`SSLeay_version` C API + for details. + + +.. py:function:: SSLeay_version(type) + + Retrieve a string describing some aspect of the underlying OpenSSL version. The + type passed in should be one of the :py:const:`SSLEAY_*` constants defined in + this module. + + +.. py:data:: ContextType + + See :py:class:`Context`. + + +.. py:class:: Context(method) + + A class representing SSL contexts. Contexts define the parameters of one or + more SSL connections. + + *method* should be :py:const:`SSLv2_METHOD`, :py:const:`SSLv3_METHOD`, + :py:const:`SSLv23_METHOD`, :py:const:`TLSv1_METHOD`, :py:const:`TLSv1_1_METHOD`, + or :py:const:`TLSv1_2_METHOD`. + + +.. py:class:: Session() + + A class representing an SSL session. A session defines certain connection + parameters which may be re-used to speed up the setup of subsequent + connections. + + .. versionadded:: 0.14 + + +.. py:data:: ConnectionType + + See :py:class:`Connection`. + + +.. py:class:: Connection(context, socket) + + A class representing SSL connections. + + *context* should be an instance of :py:class:`Context` and *socket* + should be a socket [#connection-context-socket]_ object. *socket* may be + *None*; in this case, the Connection is created with a memory BIO: see + the :py:meth:`bio_read`, :py:meth:`bio_write`, and :py:meth:`bio_shutdown` + methods. + +.. py:exception:: Error + + This exception is used as a base class for the other SSL-related + exceptions, but may also be raised directly. + + Whenever this exception is raised directly, it has a list of error messages + from the OpenSSL error queue, where each item is a tuple *(lib, function, + reason)*. Here *lib*, *function* and *reason* are all strings, describing + where and what the problem is. See :manpage:`err(3)` for more information. + + +.. py:exception:: ZeroReturnError + + This exception matches the error return code + :py:data:`SSL_ERROR_ZERO_RETURN`, and is raised when the SSL Connection has + been closed. In SSL 3.0 and TLS 1.0, this only occurs if a closure alert has + occurred in the protocol, i.e. the connection has been closed cleanly. Note + that this does not necessarily mean that the transport layer (e.g. a socket) + has been closed. + + It may seem a little strange that this is an exception, but it does match an + :py:data:`SSL_ERROR` code, and is very convenient. + + +.. py:exception:: WantReadError + + The operation did not complete; the same I/O method should be called again + later, with the same arguments. Any I/O method can lead to this since new + handshakes can occur at any time. + + The wanted read is for **dirty** data sent over the network, not the + **clean** data inside the tunnel. For a socket based SSL connection, + **read** means data coming at us over the network. Until that read + succeeds, the attempted :py:meth:`OpenSSL.SSL.Connection.recv`, + :py:meth:`OpenSSL.SSL.Connection.send`, or + :py:meth:`OpenSSL.SSL.Connection.do_handshake` is prevented or incomplete. You + probably want to :py:meth:`select()` on the socket before trying again. + + +.. py:exception:: WantWriteError + + See :py:exc:`WantReadError`. The socket send buffer may be too full to + write more data. + + +.. py:exception:: WantX509LookupError + + The operation did not complete because an application callback has asked to be + called again. The I/O method should be called again later, with the same + arguments. + + .. note:: This won't occur in this version, as there are no such + callbacks in this version. + + +.. py:exception:: SysCallError + + The :py:exc:`SysCallError` occurs when there's an I/O error and OpenSSL's + error queue does not contain any information. This can mean two things: An + error in the transport protocol, or an end of file that violates the protocol. + The parameter to the exception is always a pair *(errnum, + errstr)*. + + + +.. _openssl-context: + +Context objects +--------------- + +Context objects have the following methods: + +.. :py:class:: OpenSSL.SSL.Context + +.. py:method:: Context.check_privatekey() + + Check if the private key (loaded with :py:meth:`use_privatekey`) matches the + certificate (loaded with :py:meth:`use_certificate`). Returns + :py:data:`None` if they match, raises :py:exc:`Error` otherwise. + + +.. py:method:: Context.get_app_data() + + Retrieve application data as set by :py:meth:`set_app_data`. + + +.. py:method:: Context.get_cert_store() + + Retrieve the certificate store (a X509Store object) that the context uses. + This can be used to add "trusted" certificates without using the. + :py:meth:`load_verify_locations` method. + + +.. py:method:: Context.get_timeout() + + Retrieve session timeout, as set by :py:meth:`set_timeout`. The default is 300 + seconds. + + +.. py:method:: Context.get_verify_depth() + + Retrieve the Context object's verify depth, as set by + :py:meth:`set_verify_depth`. + + +.. py:method:: Context.get_verify_mode() + + Retrieve the Context object's verify mode, as set by :py:meth:`set_verify`. + + +.. py:method:: Context.load_client_ca(pemfile) + + Read a file with PEM-formatted certificates that will be sent to the client + when requesting a client certificate. + + +.. py:method:: Context.set_client_ca_list(certificate_authorities) + + Replace the current list of preferred certificate signers that would be + sent to the client when requesting a client certificate with the + *certificate_authorities* sequence of :py:class:`OpenSSL.crypto.X509Name`'s. + + .. versionadded:: 0.10 + + +.. py:method:: Context.add_client_ca(certificate_authority) + + Extract a :py:class:`OpenSSL.crypto.X509Name` from the *certificate_authority* + :py:class:`OpenSSL.crypto.X509` certificate and add it to the list of preferred + certificate signers sent to the client when requesting a client certificate. + + .. versionadded:: 0.10 + + +.. py:method:: Context.load_verify_locations(pemfile, capath) + + Specify where CA certificates for verification purposes are located. These + are trusted certificates. Note that the certificates have to be in PEM + format. If capath is passed, it must be a directory prepared using the + ``c_rehash`` tool included with OpenSSL. Either, but not both, of + *pemfile* or *capath* may be :py:data:`None`. + + +.. py:method:: Context.set_default_verify_paths() + + Specify that the platform provided CA certificates are to be used for + verification purposes. This method may not work properly on OS X. + + +.. py:method:: Context.load_tmp_dh(dhfile) + + Load parameters for Ephemeral Diffie-Hellman from *dhfile*. + + +.. py:method:: Context.set_app_data(data) + + Associate *data* with this Context object. *data* can be retrieved + later using the :py:meth:`get_app_data` method. + + +.. py:method:: Context.set_cipher_list(ciphers) + + Set the list of ciphers to be used in this context. See the OpenSSL manual for + more information (e.g. :manpage:`ciphers(1)`) + + +.. py:method:: Context.set_info_callback(callback) + + Set the information callback to *callback*. This function will be called + from time to time during SSL handshakes. + + *callback* should take three arguments: a Connection object and two integers. + The first integer specifies where in the SSL handshake the function was + called, and the other the return code from a (possibly failed) internal + function call. + + +.. py:method:: Context.set_options(options) + + Add SSL options. Options you have set before are not cleared! + This method should be used with the :py:const:`OP_*` constants. + + +.. py:method:: Context.set_mode(mode) + + Add SSL mode. Modes you have set before are not cleared! This method should + be used with the :py:const:`MODE_*` constants. + + +.. py:method:: Context.set_passwd_cb(callback[, userdata]) + + Set the passphrase callback to *callback*. This function will be called + when a private key with a passphrase is loaded. *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 + *userdata* parameter to :py:meth:`set_passwd_cb`. If an error occurs, + *callback* should return a false value (e.g. an empty string). + + +.. py:method:: Context.set_session_cache_mode(mode) + + Set the behavior of the session cache used by all connections using this + Context. The previously set mode is returned. See :py:const:`SESS_CACHE_*` + for details about particular modes. + + .. versionadded:: 0.14 + + +.. py:method:: Context.get_session_cache_mode() + + Get the current session cache mode. + + .. versionadded:: 0.14 + + +.. py:method:: Context.set_session_id(name) + + Set the context *name* within which a session can be reused for this + Context object. This is needed when doing session resumption, because there is + no way for a stored session to know which Context object it is associated with. + *name* may be any binary data. + + +.. py:method:: Context.set_timeout(timeout) + + Set the timeout for newly created sessions for this Context object to + *timeout*. *timeout* must be given in (whole) seconds. The default + value is 300 seconds. See the OpenSSL manual for more information (e.g. + :manpage:`SSL_CTX_set_timeout(3)`). + + +.. py:method:: Context.set_verify(mode, callback) + + Set the verification flags for this Context object to *mode* and specify + that *callback* should be used for verification callbacks. *mode* should be + one of :py:const:`VERIFY_NONE` and :py:const:`VERIFY_PEER`. If + :py:const:`VERIFY_PEER` is used, *mode* can be OR:ed with + :py:const:`VERIFY_FAIL_IF_NO_PEER_CERT` and :py:const:`VERIFY_CLIENT_ONCE` + to further control the behaviour. + + *callback* should take five arguments: A Connection object, an X509 object, + and three integer variables, which are in turn potential error number, error + depth and return code. *callback* should return true if verification passes + and false otherwise. + + +.. py:method:: Context.set_verify_depth(depth) + + Set the maximum depth for the certificate chain verification that shall be + allowed for this Context object. + + +.. py:method:: Context.use_certificate(cert) + + Use the certificate *cert* which has to be a X509 object. + + +.. py:method:: Context.add_extra_chain_cert(cert) + + Adds the certificate *cert*, which has to be a X509 object, to the + certificate chain presented together with the certificate. + + +.. py:method:: Context.use_certificate_chain_file(file) + + Load a certificate chain from *file* which must be PEM encoded. + + +.. py:method:: Context.use_privatekey(pkey) + + Use the private key *pkey* which has to be a PKey object. + + +.. py:method:: Context.use_certificate_file(file[, format]) + + Load the first certificate found in *file*. The certificate must be in the + format specified by *format*, which is either :py:const:`FILETYPE_PEM` or + :py:const:`FILETYPE_ASN1`. The default is :py:const:`FILETYPE_PEM`. + + +.. py:method:: Context.use_privatekey_file(file[, format]) + + Load the first private key found in *file*. The private key must be in the + format specified by *format*, which is either :py:const:`FILETYPE_PEM` or + :py:const:`FILETYPE_ASN1`. The default is :py:const:`FILETYPE_PEM`. + + +.. py:method:: Context.set_tlsext_servername_callback(callback) + + Specify a one-argument callable to use as the TLS extension server name + callback. When a connection using the server name extension is made using + this context, the callback will be invoked with the :py:class:`Connection` + instance. + + .. versionadded:: 0.13 + + +.. _openssl-session: + +Session objects +--------------- + +Session objects have no methods. + + +.. _openssl-connection: + +Connection objects +------------------ + +Connection objects have the following methods: + +.. py:method:: Connection.accept() + + Call the :py:meth:`accept` method of the underlying socket and set up SSL on the + returned socket, using the Context object supplied to this Connection object at + creation. Returns a pair *(conn, address)*. where *conn* is the new + Connection object created, and *address* is as returned by the socket's + :py:meth:`accept`. + + +.. py:method:: Connection.bind(address) + + Call the :py:meth:`bind` method of the underlying socket. + + +.. py:method:: Connection.close() + + Call the :py:meth:`close` method of the underlying socket. Note: If you want + correct SSL closure, you need to call the :py:meth:`shutdown` method first. + + +.. py:method:: Connection.connect(address) + + Call the :py:meth:`connect` method of the underlying socket and set up SSL on the + socket, using the Context object supplied to this Connection object at + creation. + + +.. py:method:: Connection.connect_ex(address) + + Call the :py:meth:`connect_ex` method of the underlying socket and set up SSL on + the socket, using the Context object supplied to this Connection object at + creation. Note that if the :py:meth:`connect_ex` method of the socket doesn't + return 0, SSL won't be initialized. + + +.. py:method:: Connection.do_handshake() + + Perform an SSL handshake (usually called after :py:meth:`renegotiate` or one of + :py:meth:`set_accept_state` or :py:meth:`set_accept_state`). This can raise the + same exceptions as :py:meth:`send` and :py:meth:`recv`. + + +.. py:method:: Connection.fileno() + + Retrieve the file descriptor number for the underlying socket. + + +.. py:method:: Connection.listen(backlog) + + Call the :py:meth:`listen` method of the underlying socket. + + +.. py:method:: Connection.get_app_data() + + Retrieve application data as set by :py:meth:`set_app_data`. + + +.. py:method:: Connection.get_cipher_list() + + Retrieve the list of ciphers used by the Connection object. WARNING: This API + has changed. It used to take an optional parameter and just return a string, + but not it returns the entire list in one go. + + +.. py:method:: Connection.get_client_ca_list() + + Retrieve the list of preferred client certificate issuers sent by the server + as :py:class:`OpenSSL.crypto.X509Name` objects. + + If this is a client :py:class:`Connection`, the list will be empty until the + connection with the server is established. + + If this is a server :py:class:`Connection`, return the list of certificate + authorities that will be sent or has been sent to the client, as controlled + by this :py:class:`Connection`'s :py:class:`Context`. + + .. versionadded:: 0.10 + + +.. py:method:: Connection.get_context() + + Retrieve the Context object associated with this Connection. + + +.. py:method:: Connection.set_context(context) + + Specify a replacement Context object for this Connection. + + +.. py:method:: Connection.get_peer_certificate() + + Retrieve the other side's certificate (if any) + + +.. py:method:: Connection.get_peer_cert_chain() + + Retrieve the tuple of the other side's certificate chain (if any) + + +.. py:method:: Connection.getpeername() + + Call the :py:meth:`getpeername` method of the underlying socket. + + +.. py:method:: Connection.getsockname() + + Call the :py:meth:`getsockname` method of the underlying socket. + + +.. py:method:: Connection.getsockopt(level, optname[, buflen]) + + Call the :py:meth:`getsockopt` method of the underlying socket. + + +.. py:method:: Connection.pending() + + Retrieve the number of bytes that can be safely read from the SSL buffer + (**not** the underlying transport buffer). + + +.. py:method:: Connection.recv(bufsize) + + Receive data from the Connection. The return value is a string representing the + data received. The maximum amount of data to be received at once, is specified + by *bufsize*. + + +.. py:method:: Connection.bio_write(bytes) + + If the Connection was created with a memory BIO, this method can be used to add + bytes to the read end of that memory BIO. The Connection can then read the + bytes (for example, in response to a call to :py:meth:`recv`). + + +.. py:method:: Connection.renegotiate() + + Renegotiate the SSL session. Call this if you wish to change cipher suites or + anything like that. + + +.. py:method:: Connection.send(string) + + Send the *string* data to the Connection. + + +.. py:method:: Connection.bio_read(bufsize) + + If the Connection was created with a memory BIO, this method can be used to + read bytes from the write end of that memory BIO. Many Connection methods will + add bytes which must be read in this manner or the buffer will eventually fill + up and the Connection will be able to take no further actions. + + +.. py:method:: Connection.sendall(string) + + Send all of the *string* data to the Connection. This calls :py:meth:`send` + repeatedly until all data is sent. If an error occurs, it's impossible to tell + how much data has been sent. + + +.. py:method:: Connection.set_accept_state() + + Set the connection to work in server mode. The handshake will be handled + automatically by read/write. + + +.. py:method:: Connection.set_app_data(data) + + Associate *data* with this Connection object. *data* can be retrieved + later using the :py:meth:`get_app_data` method. + + +.. py:method:: Connection.set_connect_state() + + Set the connection to work in client mode. The handshake will be handled + automatically by read/write. + + +.. py:method:: Connection.setblocking(flag) + + Call the :py:meth:`setblocking` method of the underlying socket. + + +.. py:method:: Connection.setsockopt(level, optname, value) + + Call the :py:meth:`setsockopt` method of the underlying socket. + + +.. py:method:: Connection.shutdown() + + Send the shutdown message to the Connection. Returns true if the shutdown + message exchange is completed and false otherwise (in which case you call + :py:meth:`recv` or :py:meth:`send` when the connection becomes + readable/writeable. + + +.. py:method:: Connection.get_shutdown() + + Get the shutdown state of the Connection. Returns a bitvector of either or + both of *SENT_SHUTDOWN* and *RECEIVED_SHUTDOWN*. + + +.. py:method:: Connection.set_shutdown(state) + + Set the shutdown state of the Connection. *state* is a bitvector of + either or both of *SENT_SHUTDOWN* and *RECEIVED_SHUTDOWN*. + + +.. py:method:: Connection.sock_shutdown(how) + + Call the :py:meth:`shutdown` method of the underlying socket. + + +.. py:method:: Connection.bio_shutdown() + + If the Connection was created with a memory BIO, this method can be used to + indicate that *end of file* has been reached on the read end of that memory + BIO. + + +.. py:method:: Connection.state_string() + + Retrieve a verbose string detailing the state of the Connection. + + +.. py:method:: Connection.client_random() + + Retrieve the random value used with the client hello message. + + +.. py:method:: Connection.server_random() + + Retrieve the random value used with the server hello message. + + +.. py:method:: Connection.master_key() + + Retrieve the value of the master key for this session. + + +.. py:method:: Connection.want_read() + + Checks if more data has to be read from the transport layer to complete an + operation. + + +.. py:method:: Connection.want_write() + + Checks if there is data to write to the transport layer to complete an + operation. + + +.. py:method:: Connection.set_tlsext_host_name(name) + + Specify the byte string to send as the server name in the client hello message. + + .. versionadded:: 0.13 + + +.. py:method:: Connection.get_servername() + + Get the value of the server name received in the client hello message. + + .. versionadded:: 0.13 + + +.. py:method:: Connection.get_session() + + Get a :py:class:`Session` instance representing the SSL session in use by + the connection, or :py:obj:`None` if there is no session. + + .. versionadded:: 0.14 + + +.. py:method:: Connection.set_session(session) + + Set a new SSL session (using a :py:class:`Session` instance) to be used by + the connection. + + .. versionadded:: 0.14 + + +.. Rubric:: Footnotes + +.. [#connection-context-socket] Actually, all that is required is an object that + **behaves** like a socket, you could even use files, even though it'd be + tricky to get the handshakes right! diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..d9c9a67 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,219 @@ +# -*- coding: utf-8 -*- +# +# pyOpenSSL documentation build configuration file, created by +# sphinx-quickstart on Sat Jul 16 07:12:22 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +DOC_DIR = os.path.abspath(os.path.dirname(__file__)) +sys.path.insert(0, os.path.abspath(os.path.join(DOC_DIR, ".."))) + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'pyOpenSSL' +copyright = u'2011, Jean-Paul Calderone' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.14' +# The full version, including alpha/beta/rc tags. +release = '0.14' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'pyOpenSSLdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'pyOpenSSL.tex', u'pyOpenSSL Documentation', + u'Jean-Paul Calderone', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'pyopenssl', u'pyOpenSSL Documentation', + [u'Jean-Paul Calderone'], 1) +] diff --git a/doc/images/pyopenssl-brand.png b/doc/images/pyopenssl-brand.png new file mode 100644 index 0000000000000000000000000000000000000000..9c461857a26de0f1bed561c45f321df27d190fa2 GIT binary patch literal 3636 zcmb_fcQhN`+fRsDJGH6_s%l54Sz@+ksno8mN-1iqB34VaMtu=mDXMDk5`wB3<7cG^ zQ9|w5LGb4L_j`VSyyv{nbMC$8Jm-$*x#xL4_jBWnA3bDczQPOu09bW(v`nZY=HJFh zPwh=+Js7A1y`%m^Ex^UUnvX6?p<0-{bu4`WfJ@+i8xWA0%||sd_~{yIGvGj%F5eP5 zysmga-LXSgOZ`dE^d>&s+mx8ovvbtkhn4N6Ghk#Ax~tB@aa)L!D>~sP}Oe_aNW9&WwQ^F!(Y?+hFU`a1h*M*pc$P2{8P6+&YrBw$g^YV z7tO+V!ARjzV2PeB(oF0Dhac2cVD4g-xzqFj?cHe(d~m)3gsJeat-sFZDW&@0ey=*b@-*lF3j~9 za(T>Jjkp}47&tOF_b4PJB(R~}u4xN3kS-gz;?C4a9{fz2lt}tthZlg1d3CPGoALS? zU7Vls-+OVnudnaAsOV^kk>F6S%0MZi#MRAh0TV%sM+b}QAxQ*p1(a&2QgCpvvx|!( zpM0L853!&7aO1b;%a=0S=p!fu0$Ib{QL+FrC( zS0|TSH=0;m3k<5>2YoP!l>)YYN>1+RPnFWu)!k|Sj&BJ>n3{HX$MdEFu7KXPQGM8@ z>Z`b)QrODL8p35Dt{Hq2^T zOi-4tbf2oQ`EFG&SqH`#-ZrKi1pG(Z(@D9C{cC66x)L38sHmv1HIL&bA(6fTAff)E!)Nvl z)qS;?n3%NOqrhim(!$d6@>|EiK|o|^(6=Wvppm7%6p?RpGfncK>6=#q?ei8BaHSj~ z4?frvFcIpdA%A|x;XLH-Lyg4!`$)MISC?G_9u_Oim#`F_ctfY7V?1SHRuG4)N=|tx zhL#y>dJS9xLx9q0IXRHJA7C(8*T{&y9=7ws{!gqAY43a9-cWR)IMihDZ(HH2*YUH^owRyP1^gag1cqe z;VmThq-RqX=d6Dr7Z@xvr?y2fJRi-f?c566Vdx4QIjc77WOQN8?yk%X=XPNtxCoJk zF@>PlG4;;tWeTAue#Xk@ldGCpnywrm7zRq>V zfyE-N!Vw6B#o6U2|cCNk7 zJ4kk=ZyDO$wJZ9F`56Dw**jN4Mdmg$QoB`m#oAJ%i;f^4p~!2buP;Is`>exh_sX5) z6=2Y050H{m?-a3?GC|oI+-@3w#gw@Exog9IB_oGYu){0;yFEIJm;FQ*Dv2~lLd-?{ zd++55TwQ_(wTo#BH1X@Mtovy^1RGyPeY?uWAlpicoQFoYq>q6YD9R=Xz)9E#~gTg?n-V*f|PlMg_V($lgRp1 zpcG=Q1`+MrFbZ{n}qhFm~I<#pkT> zcz*Se!j8j3X8IzZ4Z6oDbcRkv?5#7_C%!Z;!IQSBs$)lnN!zyKwu#f50aRi@(=6_(MpW=YW!+A{`jD-(y7bL%y3tp-`S5TXCF8;+LKQ@j z9RchY?Fr-EH&BD$_$=n5vhOK-U2Dl;rl>l`F;V{jPm(M3C$K z)2uf7;4XZKd<))B=6w8MoHbAIM^Nma(C+gb))R>w@IqTyXeXvmbA10x@6hVVqdA7T z*zt?AcP#v!UF>iVHwckP#Cf2fb7x`;39YdVsqp-b;in+u~PyC9owS_jE9X?x6 zuM2JsNjRz1w>6Y*)les8nU2(E@Fi*Oyx1 z9|YtRT3hc^!R>q55=}`&-s_!sud$D%JL%Ygjh3p!kgG?2ErgT3&n+wlT5Y1YRaA05 z3dqwR)Hov$;6(Vj!sh0tndt)F`WoKjb5@`oRN#b9HX*p$x*O`H@@SI5GJ9KL9-lTF zm4d|IjO!(irps9K?7Q!lmX==T;d%S)0_{f;eeoz@yA5%C?&B2+CtQO8iw!b5an+x9 zIA$rwdtHYrK<#kJKk_p2b}h?KXB5*mtUD7B${MeguSo(o0+gu_y6{*onH;32r>9Y* zXlwXf`FFr4#gOGH?`@5_7h&;lE0uV$<$)}D479xH z5jZ|R_Ly(WF@m4n#Tl-SB2H&A8>-9QW+s3|5vw7Tu+Jh$I_iUqlJ~OxYZG&mB~8{2 z%4~Std!!jV+!20RtRxk{BQ7B!41>Y0&!lB!=!S)bRb^Gv;sloDDDl4lJbY9($9|ur zrqfv6)P!4ENecb5#B59hTE{>30%lQHlUAG(%yJ8`d2o2sNAKNRId=eaUwn0^)mU2D zX$QP_A=wcSz{8(HLDO|^AFe9imY%k{UHIFpS?ro<}pi;r#1+ zF7V4<_{ybu40~YY;^JcPF)_Vs%_@Y-3*9HlL}J1mo+-f?eRA2gBa+S`uyD2QbW228 z**dutL^6EwLbCSxFxx=GQA3YdDnQ-X%$zJ zt{;|^X)fk=l*^fVbysBV^!A6pPQCNayT^0k^MVJS@ALh9Ki|*i?c?|Je7^@05)u*; z5)u*;5(^TUFN5Rb<72Y3v#WsBfLw%lOifKqd+O@ye)5+9J_W7U>yH4b!0W&w5w6(e zI(W+d%H#y*qwyu~^c90l);@oNtk)k^|WN zbXCrEG7J7+V8II@+Lxd+G#U*V85zXJ#-dOtFquqTx^#(_mX`U>d+r4=7!2g+=MxYx zv9Z%=G^D1cQeR(Bd3ia*!^88P|J(?Wl$1naVIgA`Vk+y`ug7dQQ&m+pJI8OJpokK5 ze85Z_rgpkGL@9mlH)eApKu%7M%jCz%%*>>wriOum0nZ&5k&BK4Ztda$BXaigsXJ5A z7r}^#2s9dvTRTAzNK8zet^0Qz8)+eF+*^B(o^?Iszm-2#gyo(%oOlDau#z}30VVL# z<%pYTx!~I2k|j$#&L%iGcxHnHv~1>!@pc@^j@{J>)kFqH0&pzu80Y)W^YF#Pv2Ksf z6h;Iv@;N#V0`8BZ24N-v`uqDm7Qn4-6p83?`kDwbfe=N=L|ZNpDug-;uo5HRkKRAx zGUYV#`&2)2wvdF5LmnmX0lmGwm`oJ$T-EIfqbnj{IJiFs) z@+La6(UHv>KBk19iD%I?A=yF+BCiG2)zuh{#u*pFbLY-sHk*B#r}U3fPX2QeRe*}- zC(YQgPd7RP`~ZAQREfZ;Xt0j96D%;W*CT)9F;MFlpS&1ru_L&NM#pbKt4 zy-m}TCg;fmdBBoRMEGM)@1sYT&yTxDO+*e*0X^tJP}JYPHjzt5hnsZrv*0M0Pj*rvQfv83l=7 zLgP)SD}od`jZ}-Bd_X_|1qB5z`?IpL2oLxD=zneST5hemMOSJUJ0f>DH(Cem1;ADs z&fA5cECl5N*wh18O3{lJmj2MY03kU>WxSVwuH4y2lTcuL5efxH|!Kb98 zuwumur|X2nT#tbshbLj9DIv%N!j%(mky5TCOc_Sc%W?BMz9Jof+YIC4i`$!DU~+&e zSDm|e?BeWw=p_mb!_avx1Xfnl1RIrhT(=`aw&W?+14)t6ciLp z+W&jP&Ye363JUVfxRENL)A7+Y%Qfl;>KS5ES?%RVj(FlywC!+max$w{t(x^XwOY-# zZQH1<^x*vSGWYq2cZp*$uK##;I + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..e4a5a23 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,26 @@ +Welcome to pyOpenSSL's documentation! +===================================== + +.. topic:: Abstract + + This module is a rather thin wrapper around (a subset of) the OpenSSL library. + With thin wrapper I mean that a lot of the object methods do nothing more than + calling a corresponding function in the OpenSSL library. + + +Contents: + +.. toctree:: + :maxdepth: 3 + + introduction + api + internals + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/doc/internals.rst b/doc/internals.rst new file mode 100644 index 0000000..a2a4cdc --- /dev/null +++ b/doc/internals.rst @@ -0,0 +1,69 @@ +.. _internals: + +Internals +========= + +We ran into three main problems developing this: Exceptions, callbacks and +accessing socket methods. This is what this chapter is about. + + +.. _exceptions: + +Exceptions +---------- + +We realized early that most of the exceptions would be raised by the I/O +functions of OpenSSL, so it felt natural to mimic OpenSSL's error code system, +translating them into Python exceptions. This naturally gives us the exceptions +:py:exc:`.SSL.ZeroReturnError`, :py:exc:`.SSL.WantReadError`, +:py:exc:`.SSL.WantWriteError`, :py:exc:`.SSL.WantX509LookupError` and +:py:exc:`.SSL.SysCallError`. + +For more information about this, see section :ref:`openssl-ssl`. + + +.. _callbacks: + +Callbacks +--------- + +Callbacks were more of a problem when pyOpenSSL was written in C. +Having switched to being written in Python using cffi, callbacks are now straightforward. +The problems that originally existed no longer do +(if you are interested in the details you can find descriptions of those problems in the version control history for this document). + +.. _socket-methods: + +Accessing Socket Methods +------------------------ + +We quickly saw the benefit of wrapping socket methods in the +:py:class:`.SSL.Connection` class, for an easy transition into using SSL. The +problem here is that the :py:mod:`socket` module lacks a C API, and all the +methods are declared static. One approach would be to have :py:mod:`.OpenSSL` as +a submodule to the :py:mod:`socket` module, placing all the code in +``socketmodule.c``, but this is obviously not a good solution, since you +might not want to import tonnes of extra stuff you're not going to use when +importing the :py:mod:`socket` module. The other approach is to somehow get a +pointer to the method to be called, either the C function, or a callable Python +object. This is not really a good solution either, since there's a lot of +lookups involved. + +The way it works is that you have to supply a :py:class:`socket`- **like** transport +object to the :py:class:`.SSL.Connection`. The only requirement of this object is +that it has a :py:meth:`fileno()` method that returns a file descriptor that's +valid at the C level (i.e. you can use the system calls read and write). If you +want to use the :py:meth:`connect()` or :py:meth:`accept()` methods of the +:py:class:`.SSL.Connection` object, the transport object has to supply such +methods too. Apart from them, any method lookups in the :py:class:`.SSL.Connection` +object that fail are passed on to the underlying transport object. + +Future changes might be to allow Python-level transport objects, that instead +of having :py:meth:`fileno()` methods, have :py:meth:`read()` and :py:meth:`write()` +methods, so more advanced features of Python can be used. This would probably +entail some sort of OpenSSL **BIOs**, but converting Python strings back and +forth is expensive, so this shouldn't be used unless necessary. Other nice +things would be to be able to pass in different transport objects for reading +and writing, but then the :py:meth:`fileno()` method of :py:class:`.SSL.Connection` +becomes virtually useless. Also, should the method resolution be used on the +read-transport or the write-transport? diff --git a/doc/introduction.rst b/doc/introduction.rst new file mode 100644 index 0000000..c29f80c --- /dev/null +++ b/doc/introduction.rst @@ -0,0 +1,17 @@ +.. _intro: + +Introduction +============ + +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 `_) +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. diff --git a/doc/make.bat b/doc/make.bat new file mode 100644 index 0000000..5ef04de --- /dev/null +++ b/doc/make.bat @@ -0,0 +1,170 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pyOpenSSL.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pyOpenSSL.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/doc/pyOpenSSL.tex b/doc/pyOpenSSL.tex deleted file mode 100644 index 99e3479..0000000 --- a/doc/pyOpenSSL.tex +++ /dev/null @@ -1,1451 +0,0 @@ -\documentclass{howto} - -\title{Python OpenSSL Manual} - -\release{0.13} - -\author{Jean-Paul Calderone} -\authoraddress{\email{exarkun@twistedmatrix.com}} - -\usepackage[english]{babel} -\usepackage[T1]{fontenc} - -\begin{document} - -\maketitle - -\begin{abstract} -\noindent -This module is a rather thin wrapper around (a subset of) the OpenSSL library. -With thin wrapper I mean that a lot of the object methods do nothing more than -calling a corresponding function in the OpenSSL library. -\end{abstract} - -\tableofcontents - - -\section{Introduction \label{intro}} - -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}} - -These instructions can also be found in the file \verb|INSTALL|. - -I have tested this on Debian Linux systems (woody and sid), Solaris 2.6 and -2.7. Others have successfully compiled it on Windows and NT. - -\subsection{Building the Module on a Unix System \label{building-unix}} - -pyOpenSSL uses distutils, so there really shouldn't be any problems. To build -the library: -\begin{verbatim} -python setup.py build -\end{verbatim} - -If your OpenSSL header files aren't in \verb|/usr/include|, you may need to -supply the \verb|-I| flag to let the setup script know where to look. The same -goes for the libraries of course, use the \verb|-L| flag. Note that -\verb|build| won't accept these flags, so you have to run first -\verb|build_ext| and then \verb|build|! Example: -\begin{verbatim} -python setup.py build_ext -I/usr/local/ssl/include -L/usr/local/ssl/lib -python setup.py build -\end{verbatim} - -Now you should have a directory called \verb|OpenSSL| that contains e.g. -\verb|SSL.so| and \verb|__init__.py| somewhere in the build dicrectory, -so just: -\begin{verbatim} -python setup.py install -\end{verbatim} - -If you, for some arcane reason, don't want the module to appear in the -\verb|site-packages| directory, use the \verb|--prefix| option. - -You can, of course, do -\begin{verbatim} -python setup.py --help -\end{verbatim} - -to find out more about how to use the script. - -\subsection{Building the Module on a Windows System \label{building-windows}} - -Big thanks to Itamar Shtull-Trauring and Oleg Orlov for their help with -Windows build instructions. Same as for Unix systems, we have to separate -the \verb|build_ext| and the \verb|build|. - -Building the library: - -\begin{verbatim} -setup.py build_ext -I ...\openssl\inc32 -L ...\openssl\out32dll -setup.py build -\end{verbatim} - -Where \verb|...\openssl| is of course the location of your OpenSSL installation. - -Installation is the same as for Unix systems: -\begin{verbatim} -setup.py install -\end{verbatim} - -And similarily, you can do -\begin{verbatim} -setup.py --help -\end{verbatim} - -to get more information. - - -\section{\module{OpenSSL} --- Python interface to OpenSSL \label{openssl}} - -\declaremodule{extension}{OpenSSL} -\modulesynopsis{Python interface to OpenSSL} - -This package provides a high-level interface to the functions in the -OpenSSL library. The following modules are defined: - -\begin{datadesc}{crypto} -Generic cryptographic module. Note that if anything is incomplete, this module is! -\end{datadesc} - -\begin{datadesc}{rand} -An interface to the OpenSSL pseudo random number generator. -\end{datadesc} - -\begin{datadesc}{SSL} -An interface to the SSL-specific parts of OpenSSL. -\end{datadesc} - - -% % % crypto moduleOpenSSL - -\subsection{\module{crypto} --- Generic cryptographic module \label{openssl-crypto}} - -\declaremodule{extension}{crypto} -\modulesynopsis{Generic cryptographic module} - -\begin{datadesc}{X509Type} -See \class{X509}. -\end{datadesc} - -\begin{classdesc}{X509}{} -A class representing X.509 certificates. -\end{classdesc} - -\begin{datadesc}{X509NameType} -See \class{X509Name}. -\end{datadesc} - -\begin{classdesc}{X509Name}{x509name} -A class representing X.509 Distinguished Names. - -This constructor creates a copy of \var{x509name} which should be an -instance of \class{X509Name}. -\end{classdesc} - -\begin{datadesc}{X509ReqType} -See \class{X509Req}. -\end{datadesc} - -\begin{classdesc}{X509Req}{} -A class representing X.509 certificate requests. -\end{classdesc} - -\begin{datadesc}{X509StoreType} -A Python type object representing the X509Store object type. -\end{datadesc} - -\begin{datadesc}{PKeyType} -See \class{PKey}. -\end{datadesc} - -\begin{classdesc}{PKey}{} -A class representing DSA or RSA keys. -\end{classdesc} - -\begin{datadesc}{PKCS7Type} -A Python type object representing the PKCS7 object type. -\end{datadesc} - -\begin{datadesc}{PKCS12Type} -A Python type object representing the PKCS12 object type. -\end{datadesc} - -\begin{datadesc}{X509ExtensionType} -See \class{X509Extension}. -\end{datadesc} - -\begin{classdesc}{X509Extension}{typename, critical, value\optional{, subject}\optional{, issuer}} -A class representing an X.509 v3 certificate extensions. -See \url{http://openssl.org/docs/apps/x509v3_config.html\#STANDARD_EXTENSIONS} -for \var{typename} strings and their options. -Optional parameters \var{subject} and \var{issuer} must be X509 objects. -\end{classdesc} - -\begin{datadesc}{NetscapeSPKIType} -See \class{NetscapeSPKI}. -\end{datadesc} - -\begin{classdesc}{NetscapeSPKI}{\optional{enc}} -A class representing Netscape SPKI objects. - -If the \var{enc} argument is present, it should be a base64-encoded string -representing a NetscapeSPKI object, as returned by the \method{b64_encode} -method. -\end{classdesc} - -\begin{classdesc}{CRL}{} -A class representing Certifcate Revocation List objects. -\end{classdesc} - -\begin{classdesc}{Revoked}{} -A class representing Revocation objects of CRL. -\end{classdesc} - -\begin{datadesc}{FILETYPE_PEM} -\dataline{FILETYPE_ASN1} -File type constants. -\end{datadesc} - -\begin{datadesc}{TYPE_RSA} -\dataline{TYPE_DSA} -Key type constants. -\end{datadesc} - -\begin{excdesc}{Error} -Generic exception used in the \module{crypto} module. -\end{excdesc} - -\begin{funcdesc}{dump_certificate}{type, cert} -Dump the certificate \var{cert} into a buffer string encoded with the type -\var{type}. -\end{funcdesc} - -\begin{funcdesc}{dump_certificate_request}{type, req} -Dump the certificate request \var{req} into a buffer string encoded with the -type \var{type}. -\end{funcdesc} - -\begin{funcdesc}{dump_privatekey}{type, pkey\optional{, cipher, passphrase}} -Dump the private key \var{pkey} into a buffer string encoded with the type -\var{type}, optionally (if \var{type} is \constant{FILETYPE_PEM}) encrypting it -using \var{cipher} and \var{passphrase}. - -\var{passphrase} must be either a string or a callback for providing the -pass phrase. -\end{funcdesc} - -\begin{funcdesc}{load_certificate}{type, buffer} -Load a certificate (X509) from the string \var{buffer} encoded with the -type \var{type}. -\end{funcdesc} - -\begin{funcdesc}{load_certificate_request}{type, buffer} -Load a certificate request (X509Req) from the string \var{buffer} encoded with -the type \var{type}. -\end{funcdesc} - -\begin{funcdesc}{load_privatekey}{type, buffer\optional{, passphrase}} -Load a private key (PKey) from the string \var{buffer} encoded with -the type \var{type} (must be one of \constant{FILETYPE_PEM} and -\constant{FILETYPE_ASN1}). - -\var{passphrase} must be either a string or a callback for providing the -pass phrase. -\end{funcdesc} - -\begin{funcdesc}{load_crl}{type, buffer} -Load Certificate Revocation List (CRL) data from a string \var{buffer}. -\var{buffer} encoded with the type \var{type}. The type \var{type} -must either \constant{FILETYPE_PEM} or \constant{FILETYPE_ASN1}). -\end{funcdesc} - -\begin{funcdesc}{load_pkcs7_data}{type, buffer} -Load pkcs7 data from the string \var{buffer} encoded with the type \var{type}. -\end{funcdesc} - -\begin{funcdesc}{load_pkcs12}{buffer\optional{, passphrase}} -Load pkcs12 data from the string \var{buffer}. If the pkcs12 structure is -encrypted, a \var{passphrase} must be included. The MAC is always -checked and thus required. - -See also the man page for the C function \function{PKCS12_parse}. -\end{funcdesc} - -\begin{funcdesc}{sign}{key, data, digest} -Sign a data string using the given key and message digest. - -\var{key} is a \code{PKey} instance. \var{data} is a \code{str} instance. -\var{digest} is a \code{str} naming a supported message digest type, for example -\code{``sha1''}. -\versionadded{0.11} -\end{funcdesc} - -\begin{funcdesc}{verify}{certificate, signature, data, digest} -Verify the signature for a data string. - -\var{certificate} is a \code{X509} instance corresponding to the private key -which generated the signature. \var{signature} is a \var{str} instance giving -the signature itself. \var{data} is a \var{str} instance giving the data to -which the signature applies. \var{digest} is a \var{str} instance naming the -message digest type of the signature, for example \code{``sha1''}. -\versionadded{0.11} -\end{funcdesc} - -\subsubsection{X509 objects \label{openssl-x509}} - -X509 objects have the following methods: - -\begin{methoddesc}[X509]{get_issuer}{} -Return an X509Name object representing the issuer of the certificate. -\end{methoddesc} - -\begin{methoddesc}[X509]{get_pubkey}{} -Return a PKey object representing the public key of the certificate. -\end{methoddesc} - -\begin{methoddesc}[X509]{get_serial_number}{} -Return the certificate serial number. -\end{methoddesc} - -\begin{methoddesc}[X509]{get_signature_algorithm}{} -Return the signature algorithm used in the certificate. If the algorithm is -undefined, raise \code{ValueError}. -\end{methoddesc} - -\begin{methoddesc}[X509]{get_subject}{} -Return an X509Name object representing the subject of the certificate. -\end{methoddesc} - -\begin{methoddesc}[X509]{get_version}{} -Return the certificate version. -\end{methoddesc} - -\begin{methoddesc}[X509]{get_notBefore}{} -Return a string giving the time before which the certificate is not valid. The -string is formatted as an ASN1 GENERALIZEDTIME: -\begin{verbatim} - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm -\end{verbatim} -If no value exists for this field, \code{None} is returned. -\end{methoddesc} - -\begin{methoddesc}[X509]{get_notAfter}{} -Return a string giving the time after which the certificate is not valid. The -string is formatted as an ASN1 GENERALIZEDTIME: -\begin{verbatim} - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm -\end{verbatim} -If no value exists for this field, \code{None} is returned. -\end{methoddesc} - -\begin{methoddesc}[X509]{set_notBefore}{when} -Change the time before which the certificate is not valid. \var{when} is a -string formatted as an ASN1 GENERALIZEDTIME: -\begin{verbatim} - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm -\end{verbatim} -\end{methoddesc} - -\begin{methoddesc}[X509]{set_notAfter}{when} -Change the time after which the certificate is not valid. \var{when} is a -string formatted as an ASN1 GENERALIZEDTIME: -\begin{verbatim} - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm -\end{verbatim} -\end{methoddesc} - -\begin{methoddesc}[X509]{gmtime_adj_notBefore}{time} -Adjust the timestamp (in GMT) when the certificate starts being valid. -\end{methoddesc} - -\begin{methoddesc}[X509]{gmtime_adj_notAfter}{time} -Adjust the timestamp (in GMT) when the certificate stops being valid. -\end{methoddesc} - -\begin{methoddesc}[X509]{has_expired}{} -Checks the certificate's time stamp against current time. Returns true if the -certificate has expired and false otherwise. -\end{methoddesc} - -\begin{methoddesc}[X509]{set_issuer}{issuer} -Set the issuer of the certificate to \var{issuer}. -\end{methoddesc} - -\begin{methoddesc}[X509]{set_pubkey}{pkey} -Set the public key of the certificate to \var{pkey}. -\end{methoddesc} - -\begin{methoddesc}[X509]{set_serial_number}{serialno} -Set the serial number of the certificate to \var{serialno}. -\end{methoddesc} - -\begin{methoddesc}[X509]{set_subject}{subject} -Set the subject of the certificate to \var{subject}. -\end{methoddesc} - -\begin{methoddesc}[X509]{set_version}{version} -Set the certificate version to \var{version}. -\end{methoddesc} - -\begin{methoddesc}[X509]{sign}{pkey, digest} -Sign the certificate, using the key \var{pkey} and the message digest algorithm -identified by the string \var{digest}. -\end{methoddesc} - -\begin{methoddesc}[X509]{subject_name_hash}{} -Return the hash of the certificate subject. -\end{methoddesc} - -\begin{methoddesc}[X509]{digest}{digest_name} -Return a digest of the certificate, using the \var{digest_name} method. -\var{digest_name} must be a string describing a digest algorithm supported -by OpenSSL (by EVP_get_digestbyname, specifically). For example, -\constant{"md5"} or \constant{"sha1"}. -\end{methoddesc} - -\begin{methoddesc}[X509]{add_extensions}{extensions} -Add the extensions in the sequence \var{extensions} to the certificate. -\end{methoddesc} - -\begin{methoddesc}[X509]{get_extension_count}{} -Return the number of extensions on this certificate. -\versionadded{0.12} -\end{methoddesc} - -\begin{methoddesc}[X509]{get_extension}{index} -Retrieve the extension on this certificate at the given index. - -Extensions on a certificate are kept in order. The index parameter selects -which extension will be returned. The returned object will be an X509Extension -instance. -\versionadded{0.12} -\end{methoddesc} - -\subsubsection{X509Name objects \label{openssl-x509name}} - -X509Name objects have the following methods: - -\begin{methoddesc}[X509Name]{hash}{} -Return an integer giving the first four bytes of the MD5 digest of the DER -representation of the name. -\end{methoddesc} - -\begin{methoddesc}[X509Name]{der}{} -Return a string giving the DER representation of the name. -\end{methoddesc} - -\begin{methoddesc}[X509Name]{get_components}{} -Return a list of two-tuples of strings giving the components of the name. -\end{methoddesc} - -X509Name objects have the following members: - -\begin{memberdesc}[X509Name]{countryName} -The country of the entity. \code{C} may be used as an alias for -\code{countryName}. -\end{memberdesc} - -\begin{memberdesc}[X509Name]{stateOrProvinceName} -The state or province of the entity. \code{ST} may be used as an alias for -\code{stateOrProvinceName}· -\end{memberdesc} - -\begin{memberdesc}[X509Name]{localityName} -The locality of the entity. \code{L} may be used as an alias for -\code{localityName}. -\end{memberdesc} - -\begin{memberdesc}[X509Name]{organizationName} -The organization name of the entity. \code{O} may be used as an alias for -\code{organizationName}. -\end{memberdesc} - -\begin{memberdesc}[X509Name]{organizationalUnitName} -The organizational unit of the entity. \code{OU} may be used as an alias for -\code{organizationalUnitName}. -\end{memberdesc} - -\begin{memberdesc}[X509Name]{commonName} -The common name of the entity. \code{CN} may be used as an alias for -\code{commonName}. -\end{memberdesc} - -\begin{memberdesc}[X509Name]{emailAddress} -The e-mail address of the entity. -\end{memberdesc} - -\subsubsection{X509Req objects \label{openssl-x509req}} - -X509Req objects have the following methods: - -\begin{methoddesc}[X509Req]{get_pubkey}{} -Return a PKey object representing the public key of the certificate request. -\end{methoddesc} - -\begin{methoddesc}[X509Req]{get_subject}{} -Return an X509Name object representing the subject of the certificate. -\end{methoddesc} - -\begin{methoddesc}[X509Req]{set_pubkey}{pkey} -Set the public key of the certificate request to \var{pkey}. -\end{methoddesc} - -\begin{methoddesc}[X509Req]{sign}{pkey, digest} -Sign the certificate request, using the key \var{pkey} and the message digest -algorithm identified by the string \var{digest}. -\end{methoddesc} - -\begin{methoddesc}[X509Req]{verify}{pkey} -Verify a certificate request using the public key \var{pkey}. -\end{methoddesc} - -\begin{methoddesc}[X509Req]{set_version}{version} -Set the version (RFC 2459, 4.1.2.1) of the certificate request to -\var{version}. -\end{methoddesc} - -\begin{methoddesc}[X509Req]{get_version}{} -Get the version (RFC 2459, 4.1.2.1) of the certificate request. -\end{methoddesc} - -\subsubsection{X509Store objects \label{openssl-x509store}} - -The X509Store object has currently just one method: - -\begin{methoddesc}[X509Store]{add_cert}{cert} -Add the certificate \var{cert} to the certificate store. -\end{methoddesc} - -\subsubsection{PKey objects \label{openssl-pkey}} - -The PKey object has the following methods: - -\begin{methoddesc}[PKey]{bits}{} -Return the number of bits of the key. -\end{methoddesc} - -\begin{methoddesc}[PKey]{generate_key}{type, bits} -Generate a public/private key pair of the type \var{type} (one of -\constant{TYPE_RSA} and \constant{TYPE_DSA}) with the size \var{bits}. -\end{methoddesc} - -\begin{methoddesc}[PKey]{type}{} -Return the type of the key. -\end{methoddesc} - -\begin{methoddesc}[PKey]{check}{} -Check the consistency of this key, returning True if it is consistent and -raising an exception otherwise. This is only valid for RSA keys. See the -OpenSSL RSA_check_key man page for further limitations. -\end{methoddesc} - -\subsubsection{PKCS7 objects \label{openssl-pkcs7}} - -PKCS7 objects have the following methods: - -\begin{methoddesc}[PKCS7]{type_is_signed}{} -FIXME -\end{methoddesc} - -\begin{methoddesc}[PKCS7]{type_is_enveloped}{} -FIXME -\end{methoddesc} - -\begin{methoddesc}[PKCS7]{type_is_signedAndEnveloped}{} -FIXME -\end{methoddesc} - -\begin{methoddesc}[PKCS7]{type_is_data}{} -FIXME -\end{methoddesc} - -\begin{methoddesc}[PKCS7]{get_type_name}{} -Get the type name of the PKCS7. -\end{methoddesc} - -\subsubsection{PKCS12 objects \label{openssl-pkcs12}} - -PKCS12 objects have the following methods: - -\begin{methoddesc}[PKCS12]{export}{\optional{passphrase=None}\optional{, iter=2048}\optional{, maciter=1}} -Returns a PKCS12 object as a string. - -The optional \var{passphrase} must be a string not a callback. - -See also the man page for the C function \function{PKCS12_create}. -\end{methoddesc} - -\begin{methoddesc}[PKCS12]{get_ca_certificates}{} -Return CA certificates within the PKCS12 object as a tuple. Returns -\constant{None} if no CA certificates are present. -\end{methoddesc} - -\begin{methoddesc}[PKCS12]{get_certificate}{} -Return certificate portion of the PKCS12 structure. -\end{methoddesc} - -\begin{methoddesc}[PKCS12]{get_friendlyname}{} -Return friendlyName portion of the PKCS12 structure. -\end{methoddesc} - -\begin{methoddesc}[PKCS12]{get_privatekey}{} -Return private key portion of the PKCS12 structure -\end{methoddesc} - -\begin{methoddesc}[PKCS12]{set_ca_certificates}{cacerts} -Replace or set the CA certificates within the PKCS12 object with the sequence \var{cacerts}. - -Set \var{cacerts} to \constant{None} to remove all CA certificates. -\end{methoddesc} - -\begin{methoddesc}[PKCS12]{set_certificate}{cert} -Replace or set the certificate portion of the PKCS12 structure. -\end{methoddesc} - -\begin{methoddesc}[PKCS12]{set_friendlyname}{name} -Replace or set the friendlyName portion of the PKCS12 structure. -\end{methoddesc} - -\begin{methoddesc}[PKCS12]{set_privatekey}{pkey} -Replace or set private key portion of the PKCS12 structure -\end{methoddesc} - -\subsubsection{X509Extension objects \label{openssl-509ext}} - -X509Extension objects have several methods: - -\begin{methoddesc}[X509Extension]{get_critical}{} -Return the critical field of the extension object. -\end{methoddesc} - -\begin{methoddesc}[X509Extension]{get_short_name}{} -Retrieve the short descriptive name for this extension. - -The result is a byte string like \code{``basicConstraints''}. -\versionadded{0.12} -\end{methoddesc} - -\begin{methoddesc}[X509Extension]{get_data}{} -Retrieve the data for this extension. - -The result is the ASN.1 encoded form of the extension data as a byte string. -\versionadded{0.12} -\end{methoddesc} - -\subsubsection{NetscapeSPKI objects \label{openssl-netscape-spki}} - -NetscapeSPKI objects have the following methods: - -\begin{methoddesc}[NetscapeSPKI]{b64_encode}{} -Return a base64-encoded string representation of the object. -\end{methoddesc} - -\begin{methoddesc}[NetscapeSPKI]{get_pubkey}{} -Return the public key of object. -\end{methoddesc} - -\begin{methoddesc}[NetscapeSPKI]{set_pubkey}{key} -Set the public key of the object to \var{key}. -\end{methoddesc} - -\begin{methoddesc}[NetscapeSPKI]{sign}{key, digest_name} -Sign the NetscapeSPKI object using the given \var{key} and -\var{digest_name}. \var{digest_name} must be a string describing a digest -algorithm supported by OpenSSL (by EVP_get_digestbyname, specifically). For -example, \constant{"md5"} or \constant{"sha1"}. -\end{methoddesc} - -\begin{methoddesc}[NetscapeSPKI]{verify}{key} -Verify the NetscapeSPKI object using the given \var{key}. -\end{methoddesc} - -\subsubsection{CRL objects \label{crl}} - -CRL objects have the following methods: - -\begin{methoddesc}[CRL]{add_revoked}{revoked} -Add a Revoked object to the CRL, by value not reference. -\end{methoddesc} - -\begin{methoddesc}[CRL]{export}{cert, key\optional{, type=FILETYPE_PEM}\optional{, days=100}} -Use \var{cert} and \var{key} to sign the CRL and return the CRL as a string. -\var{days} is the number of days before the next CRL is due. -\end{methoddesc} - -\begin{methoddesc}[CRL]{get_revoked}{} -Return a tuple of Revoked objects, by value not reference. -\end{methoddesc} - -\subsubsection{Revoked objects \label{revoked}} - -Revoked objects have the following methods: - -\begin{methoddesc}[Revoked]{all_reasons}{} -Return a list of all supported reasons. -\end{methoddesc} - -\begin{methoddesc}[Revoked]{get_reason}{} -Return the revocation reason as a str. Can be -None, which differs from "Unspecified". -\end{methoddesc} - -\begin{methoddesc}[Revoked]{get_rev_date}{} -Return the revocation date as a str. -The string is formatted as an ASN1 GENERALIZEDTIME. -\end{methoddesc} - -\begin{methoddesc}[Revoked]{get_serial}{} -Return a str containing a hex number of the serial of the revoked certificate. -\end{methoddesc} - -\begin{methoddesc}[Revoked]{set_reason}{reason} -Set the revocation reason. \var{reason} must -be None or a string, but the values are limited. -Spaces and case are ignored. See \method{all_reasons}. -\end{methoddesc} - -\begin{methoddesc}[Revoked]{set_rev_date}{date} -Set the revocation date. -The string is formatted as an ASN1 GENERALIZEDTIME. -\end{methoddesc} - -\begin{methoddesc}[Revoked]{set_serial}{serial} -\var{serial} is a string containing a hex number of the serial of the revoked certificate. -\end{methoddesc} - - -% % % rand module - -\subsection{\module{rand} --- An interface to the OpenSSL pseudo random number generator \label{openssl-rand}} - -\declaremodule{extension}{rand} -\modulesynopsis{An interface to the OpenSSL pseudo random number generator} - -This module handles the OpenSSL pseudo random number generator (PRNG) and -declares the following: - -\begin{funcdesc}{add}{string, entropy} -Mix bytes from \var{string} into the PRNG state. The \var{entropy} argument is -(the lower bound of) an estimate of how much randomness is contained in -\var{string}, measured in bytes. For more information, see e.g. \rfc{1750}. -\end{funcdesc} - -\begin{funcdesc}{bytes}{num_bytes} -Get some random bytes from the PRNG as a string. - -This is a wrapper for the C function \function{RAND_bytes}. -\end{funcdesc} - -\begin{funcdesc}{cleanup}{} -Erase the memory used by the PRNG. - -This is a wrapper for the C function \function{RAND_cleanup}. -\end{funcdesc} - -\begin{funcdesc}{egd}{path\optional{, bytes}} -Query the Entropy Gathering Daemon\footnote{See -\url{http://www.lothar.com/tech/crypto/}} on socket \var{path} for \var{bytes} -bytes of random data and and uses \function{add} to seed the PRNG. The default -value of \var{bytes} is 255. -\end{funcdesc} - -\begin{funcdesc}{load_file}{path\optional{, bytes}} -Read \var{bytes} bytes (or all of it, if \var{bytes} is negative) of data from -the file \var{path} to seed the PRNG. The default value of \var{bytes} is -1. -\end{funcdesc} - -\begin{funcdesc}{screen}{} -Add the current contents of the screen to the PRNG state. -Availability: Windows. -\end{funcdesc} - -\begin{funcdesc}{seed}{string} -This is equivalent to calling \function{add} with \var{entropy} as the length -of the string. -\end{funcdesc} - -\begin{funcdesc}{status}{} -Returns true if the PRNG has been seeded with enough data, and false otherwise. -\end{funcdesc} - -\begin{funcdesc}{write_file}{path} -Write a number of random bytes (currently 1024) to the file \var{path}. This -file can then be used with \function{load_file} to seed the PRNG again. -\end{funcdesc} - -\begin{excdesc}{Error} -If the current RAND method supports any errors, this is raised when needed. -The default method does not raise this when the entropy pool is depleted. - -Whenever this exception is raised directly, it has a list of error messages -from the OpenSSL error queue, where each item is a tuple \code{(\var{lib}, -\var{function}, \var{reason})}. Here \var{lib}, \var{function} and \var{reason} -are all strings, describing where and what the problem is. See \manpage{err}{3} -for more information. -\end{excdesc} - - -% % % SSL module - -\subsection{\module{SSL} --- An interface to the SSL-specific parts of OpenSSL \label{openssl-ssl}} - -\declaremodule{extension}{SSL} -\modulesynopsis{An interface to the SSL-specific parts of OpenSSL} - -This module handles things specific to SSL. There are two objects defined: -Context, Connection. - -\begin{datadesc}{SSLv2_METHOD} -\dataline{SSLv3_METHOD} -\dataline{SSLv23_METHOD} -\dataline{TLSv1_METHOD} -These constants represent the different SSL methods to use when creating a -context object. -\end{datadesc} - -\begin{datadesc}{VERIFY_NONE} -\dataline{VERIFY_PEER} -\dataline{VERIFY_FAIL_IF_NO_PEER_CERT} -These constants represent the verification mode used by the Context -object's \method{set_verify} method. -\end{datadesc} - -\begin{datadesc}{FILETYPE_PEM} -\dataline{FILETYPE_ASN1} -File type constants used with the \method{use_certificate_file} and -\method{use_privatekey_file} methods of Context objects. -\end{datadesc} - -\begin{datadesc}{OP_SINGLE_DH_USE} -\dataline{OP_EPHEMERAL_RSA} -\dataline{OP_NO_SSLv2} -\dataline{OP_NO_SSLv3} -\dataline{OP_NO_TLSv1} -Constants used with \method{set_options} of Context objects. -\constant{OP_SINGLE_DH_USE} means to always create a new key when using ephemeral -Diffie-Hellman. \constant{OP_EPHEMERAL_RSA} means to always use ephemeral RSA keys -when doing RSA operations. \constant{OP_NO_SSLv2}, \constant{OP_NO_SSLv3} and -\constant{OP_NO_TLSv1} means to disable those specific protocols. This is -interesting if you're using e.g. \constant{SSLv23_METHOD} to get an SSLv2-compatible -handshake, but don't want to use SSLv2. -\end{datadesc} - -\begin{datadesc}{SSLEAY_VERSION} -\dataline{SSLEAY_CFLAGS} -\dataline{SSLEAY_BUILT_ON} -\dataline{SSLEAY_PLATFORM} -\dataline{SSLEAY_DIR} -Constants used with \method{SSLeay_version} to specify what OpenSSL version -information to retrieve. See the man page for the \function{SSLeay_version} C -API for details. -\end{datadesc} - -\begin{datadesc}{OPENSSL_VERSION_NUMBER} -An integer giving the version number of the OpenSSL library used to build this -version of pyOpenSSL. See the man page for the \function{SSLeay_version} C API -for details. -\end{datadesc} - -\begin{funcdesc}{SSLeay_version}{type} -Retrieve a string describing some aspect of the underlying OpenSSL version. The -type passed in should be one of the \constant{SSLEAY_*} constants defined in -this module. -\end{funcdesc} - -\begin{datadesc}{ContextType} -See \class{Context}. -\end{datadesc} - -\begin{classdesc}{Context}{method} -A class representing SSL contexts. Contexts define the parameters of one or -more SSL connections. - -\var{method} should be \constant{SSLv2_METHOD}, \constant{SSLv3_METHOD}, -\constant{SSLv23_METHOD} or \constant{TLSv1_METHOD}. -\end{classdesc} - -\begin{datadesc}{ConnectionType} -See \class{Connection}. -\end{datadesc} - -\begin{classdesc}{Connection}{context, socket} -A class representing SSL connections. - -\var{context} should be an instance of \class{Context} and \var{socket} -should be a socket \footnote{Actually, all that is required is an object -that \emph{behaves} like a socket, you could even use files, even though -it'd be tricky to get the handshakes right!} object. \var{socket} may be -\var{None}; in this case, the Connection is created with a memory BIO: see -the \method{bio_read}, \method{bio_write}, and \method{bio_shutdown} -methods. -\end{classdesc} - -\begin{excdesc}{Error} -This exception is used as a base class for the other SSL-related -exceptions, but may also be raised directly. - -Whenever this exception is raised directly, it has a list of error messages -from the OpenSSL error queue, where each item is a tuple \code{(\var{lib}, -\var{function}, \var{reason})}. Here \var{lib}, \var{function} and \var{reason} -are all strings, describing where and what the problem is. See \manpage{err}{3} -for more information. -\end{excdesc} - -\begin{excdesc}{ZeroReturnError} -This exception matches the error return code \code{SSL_ERROR_ZERO_RETURN}, and -is raised when the SSL Connection has been closed. In SSL 3.0 and TLS 1.0, this -only occurs if a closure alert has occurred in the protocol, i.e. the -connection has been closed cleanly. Note that this does not necessarily -mean that the transport layer (e.g. a socket) has been closed. - -It may seem a little strange that this is an exception, but it does match an -\code{SSL_ERROR} code, and is very convenient. -\end{excdesc} - -\begin{excdesc}{WantReadError} -The operation did not complete; the same I/O method should be called again -later, with the same arguments. Any I/O method can lead to this since new -handshakes can occur at any time. - -The wanted read is for \emph{dirty} data sent over the network, not the -\emph{clean} data inside the tunnel. For a socket based SSL connection, -\emph{read} means data coming at us over the network. Until that read -succeeds, the attempted \method{OpenSSL.SSL.Connection.recv}, -\method{OpenSSL.SSL.Connection.send}, or -\method{OpenSSL.SSL.Connection.do_handshake} is prevented or incomplete. You -probably want to \method{select()} on the socket before trying again. -\end{excdesc} - -\begin{excdesc}{WantWriteError} -See \exception{WantReadError}. The socket send buffer may be too full to -write more data. -\end{excdesc} - -\begin{excdesc}{WantX509LookupError} -The operation did not complete because an application callback has asked to be -called again. The I/O method should be called again later, with the same -arguments. Note: This won't occur in this version, as there are no such -callbacks in this version. -\end{excdesc} - -\begin{excdesc}{SysCallError} -The \exception{SysCallError} occurs when there's an I/O error and OpenSSL's -error queue does not contain any information. This can mean two things: An -error in the transport protocol, or an end of file that violates the protocol. -The parameter to the exception is always a pair \code{(\var{errnum}, -\var{errstr})}. -\end{excdesc} - - -\subsubsection{Context objects \label{openssl-context}} - -Context objects have the following methods: - -\begin{methoddesc}[Context]{check_privatekey}{} -Check if the private key (loaded with \method{use_privatekey\optional{_file}}) -matches the certificate (loaded with \method{use_certificate\optional{_file}}). -Returns \code{None} if they match, raises \exception{Error} otherwise. -\end{methoddesc} - -\begin{methoddesc}[Context]{get_app_data}{} -Retrieve application data as set by \method{set_app_data}. -\end{methoddesc} - -\begin{methoddesc}[Context]{get_cert_store}{} -Retrieve the certificate store (a X509Store object) that the context uses. -This can be used to add "trusted" certificates without using the. -\method{load_verify_locations()} method. -\end{methoddesc} - -\begin{methoddesc}[Context]{get_timeout}{} -Retrieve session timeout, as set by \method{set_timeout}. The default is 300 -seconds. -\end{methoddesc} - -\begin{methoddesc}[Context]{get_verify_depth}{} -Retrieve the Context object's verify depth, as set by -\method{set_verify_depth}. -\end{methoddesc} - -\begin{methoddesc}[Context]{get_verify_mode}{} -Retrieve the Context object's verify mode, as set by \method{set_verify}. -\end{methoddesc} - -\begin{methoddesc}[Context]{load_client_ca}{pemfile} -Read a file with PEM-formatted certificates that will be sent to the client -when requesting a client certificate. -\end{methoddesc} - -\begin{methoddesc}[Context]{set_client_ca_list}{certificate_authorities} -Replace the current list of preferred certificate signers that would be -sent to the client when requesting a client certificate with the -\var{certificate_authorities} sequence of \class{OpenSSL.crypto.X509Name}s. - -\versionadded{0.10} -\end{methoddesc} - -\begin{methoddesc}[Context]{add_client_ca}{certificate_authority} -Extract a \class{OpenSSL.crypto.X509Name} from the \var{certificate_authority} -\class{OpenSSL.crypto.X509} certificate and add it to the list of preferred -certificate signers sent to the client when requesting a client certificate. - -\versionadded{0.10} -\end{methoddesc} - -\begin{methoddesc}[Context]{load_verify_locations}{pemfile, capath} -Specify where CA certificates for verification purposes are located. These -are trusted certificates. Note that the certificates have to be in PEM -format. If capath is passed, it must be a directory prepared using the -\code{c_rehash} tool included with OpenSSL. Either, but not both, of -\var{pemfile} or \var{capath} may be \code{None}. -\end{methoddesc} - -\begin{methoddesc}[Context]{set_default_verify_paths}{} -Specify that the platform provided CA certificates are to be used for -verification purposes. This method may not work properly on OS X. -\end{methoddesc} - -\begin{methoddesc}[Context]{load_tmp_dh}{dhfile} -Load parameters for Ephemeral Diffie-Hellman from \var{dhfile}. -\end{methoddesc} - -\begin{methoddesc}[Context]{set_app_data}{data} -Associate \var{data} with this Context object. \var{data} can be retrieved -later using the \method{get_app_data} method. -\end{methoddesc} - -\begin{methoddesc}[Context]{set_cipher_list}{ciphers} -Set the list of ciphers to be used in this context. See the OpenSSL manual for -more information (e.g. ciphers(1)) -\end{methoddesc} - -\begin{methoddesc}[Context]{set_info_callback}{callback} -Set the information callback to \var{callback}. This function will be called -from time to time during SSL handshakes. -\var{callback} should take three arguments: a Connection object and two -integers. The first integer specifies where in the SSL handshake the function -was called, and the other the return code from a (possibly failed) internal -function call. -\end{methoddesc} - -\begin{methoddesc}[Context]{set_options}{options} -Add SSL options. Options you have set before are not cleared! -This method should be used with the \constant{OP_*} constants. -\end{methoddesc} - -\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} 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} -Set the context \var{name} within which a session can be reused for this -Context object. This is needed when doing session resumption, because there is -no way for a stored session to know which Context object it is associated with. -\var{name} may be any binary data. -\end{methoddesc} - -\begin{methoddesc}[Context]{set_timeout}{timeout} -Set the timeout for newly created sessions for this Context object to -\var{timeout}. \var{timeout} must be given in (whole) seconds. The default -value is 300 seconds. See the OpenSSL manual for more information (e.g. -SSL_CTX_set_timeout(3)). -\end{methoddesc} - -\begin{methoddesc}[Context]{set_verify}{mode, callback} -Set the verification flags for this Context object to \var{mode} and specify -that \var{callback} should be used for verification callbacks. \var{mode} -should be one of \constant{VERIFY_NONE} and \constant{VERIFY_PEER}. If -\constant{VERIFY_PEER} is used, \var{mode} can be OR:ed with -\constant{VERIFY_FAIL_IF_NO_PEER_CERT} and \constant{VERIFY_CLIENT_ONCE} to -further control the behaviour. -\var{callback} should take five arguments: A Connection object, an X509 object, -and three integer variables, which are in turn potential error number, error -depth and return code. \var{callback} should return true if verification passes -and false otherwise. -\end{methoddesc} - -\begin{methoddesc}[Context]{set_verify_depth}{depth} -Set the maximum depth for the certificate chain verification that shall be -allowed for this Context object. -\end{methoddesc} - -\begin{methoddesc}[Context]{use_certificate}{cert} -Use the certificate \var{cert} which has to be a X509 object. -\end{methoddesc} - -\begin{methoddesc}[Context]{add_extra_chain_cert}{cert} -Adds the certificate \var{cert}, which has to be a X509 object, to the -certificate chain presented together with the certificate. -\end{methoddesc} - -\begin{methoddesc}[Context]{use_certificate_chain_file}{file} -Load a certificate chain from \var{file} which must be PEM encoded. -\end{methoddesc} - -\begin{methoddesc}[Context]{use_privatekey}{pkey} -Use the private key \var{pkey} which has to be a PKey object. -\end{methoddesc} - -\begin{methoddesc}[Context]{use_certificate_file}{file\optional{, format}} -Load the first certificate found in \var{file}. The certificate must be in the -format specified by \var{format}, which is either \constant{FILETYPE_PEM} or -\constant{FILETYPE_ASN1}. The default is \constant{FILETYPE_PEM}. -\end{methoddesc} - -\begin{methoddesc}[Context]{use_privatekey_file}{file\optional{, format}} -Load the first private key found in \var{file}. The private key must be in the -format specified by \var{format}, which is either \constant{FILETYPE_PEM} or -\constant{FILETYPE_ASN1}. The default is \constant{FILETYPE_PEM}. -\end{methoddesc} - -\begin{methoddesc}[Context]{set_tlsext_servername_callback}{callback} -Specify a one-argument callable to use as the TLS extension server name -callback. When a connection using the server name extension is made using this -context, the callback will be invoked with the \code{Connection} instance. -\versionadded{0.13} -\end{methoddesc} - -\subsubsection{Connection objects \label{openssl-connection}} - -Connection objects have the following methods: - -\begin{methoddesc}[Connection]{accept}{} -Call the \method{accept} method of the underlying socket and set up SSL on the -returned socket, using the Context object supplied to this Connection object at -creation. Returns a pair \code{(\var{conn}, \var{address})}. where \var{conn} -is the new Connection object created, and \var{address} is as returned by the -socket's \method{accept}. -\end{methoddesc} - -\begin{methoddesc}[Connection]{bind}{address} -Call the \method{bind} method of the underlying socket. -\end{methoddesc} - -\begin{methoddesc}[Connection]{close}{} -Call the \method{close} method of the underlying socket. Note: If you want -correct SSL closure, you need to call the \method{shutdown} method first. -\end{methoddesc} - -\begin{methoddesc}[Connection]{connect}{address} -Call the \method{connect} method of the underlying socket and set up SSL on the -socket, using the Context object supplied to this Connection object at -creation. -\end{methoddesc} - -\begin{methoddesc}[Connection]{connect_ex}{address} -Call the \method{connect_ex} method of the underlying socket and set up SSL on -the socket, using the Context object supplied to this Connection object at -creation. Note that if the \method{connect_ex} method of the socket doesn't -return 0, SSL won't be initialized. -\end{methoddesc} - -\begin{methoddesc}[Connection]{do_handshake}{} -Perform an SSL handshake (usually called after \method{renegotiate} or one of -\method{set_accept_state} or \method{set_accept_state}). This can raise the -same exceptions as \method{send} and \method{recv}. -\end{methoddesc} - -\begin{methoddesc}[Connection]{fileno}{} -Retrieve the file descriptor number for the underlying socket. -\end{methoddesc} - -\begin{methoddesc}[Connection]{listen}{backlog} -Call the \method{listen} method of the underlying socket. -\end{methoddesc} - -\begin{methoddesc}[Connection]{get_app_data}{} -Retrieve application data as set by \method{set_app_data}. -\end{methoddesc} - -\begin{methoddesc}[Connection]{get_cipher_list}{} -Retrieve the list of ciphers used by the Connection object. WARNING: This API -has changed. It used to take an optional parameter and just return a string, -but not it returns the entire list in one go. -\end{methoddesc} - -\begin{methoddesc}[Connection]{get_client_ca_list}{} -Retrieve the list of preferred client certificate issuers sent by the server -as \class{OpenSSL.crypto.X509Name} objects. - -If this is a client \class{Connection}, the list will be empty until the -connection with the server is established. - -If this is a server \class{Connection}, return the list of certificate -authorities that will be sent or has been sent to the client, as controlled -by this \class{Connection}'s \class{Context}. - -\versionadded{0.10} -\end{methoddesc} - -\begin{methoddesc}[Connection]{get_context}{} -Retrieve the Context object associated with this Connection. -\end{methoddesc} - -\begin{methoddesc}[Connection]{set_context}{context} -Specify a replacement Context object for this Connection. -\end{methoddesc} - -\begin{methoddesc}[Connection]{get_peer_certificate}{} -Retrieve the other side's certificate (if any) -\end{methoddesc} - -\begin{methoddesc}[Connection]{get_peer_cert_chain}{} -Retrieve the tuple of the other side's certificate chain (if any) -\end{methoddesc} - -\begin{methoddesc}[Connection]{getpeername}{} -Call the \method{getpeername} method of the underlying socket. -\end{methoddesc} - -\begin{methoddesc}[Connection]{getsockname}{} -Call the \method{getsockname} method of the underlying socket. -\end{methoddesc} - -\begin{methoddesc}[Connection]{getsockopt}{level, optname\optional{, buflen}} -Call the \method{getsockopt} method of the underlying socket. -\end{methoddesc} - -\begin{methoddesc}[Connection]{pending}{} -Retrieve the number of bytes that can be safely read from the SSL buffer -(\emph{not} the underlying transport buffer). -\end{methoddesc} - -\begin{methoddesc}[Connection]{recv}{bufsize} -Receive data from the Connection. The return value is a string representing the -data received. The maximum amount of data to be received at once, is specified -by \var{bufsize}. -\end{methoddesc} - -\begin{methoddesc}[Connection]{bio_write}{bytes} -If the Connection was created with a memory BIO, this method can be used to add -bytes to the read end of that memory BIO. The Connection can then read the -bytes (for example, in response to a call to \method{recv}). -\end{methoddesc} - -\begin{methoddesc}[Connection]{renegotiate}{} -Renegotiate the SSL session. Call this if you wish to change cipher suites or -anything like that. -\end{methoddesc} - -\begin{methoddesc}[Connection]{send}{string} -Send the \var{string} data to the Connection. -\end{methoddesc} - -\begin{methoddesc}[Connection]{bio_read}{bufsize} -If the Connection was created with a memory BIO, this method can be used to -read bytes from the write end of that memory BIO. Many Connection methods will -add bytes which must be read in this manner or the buffer will eventually fill -up and the Connection will be able to take no further actions. -\end{methoddesc} - -\begin{methoddesc}[Connection]{sendall}{string} -Send all of the \var{string} data to the Connection. This calls \method{send} -repeatedly until all data is sent. If an error occurs, it's impossible to tell -how much data has been sent. -\end{methoddesc} - -\begin{methoddesc}[Connection]{set_accept_state}{} -Set the connection to work in server mode. The handshake will be handled -automatically by read/write. -\end{methoddesc} - -\begin{methoddesc}[Connection]{set_app_data}{data} -Associate \var{data} with this Connection object. \var{data} can be retrieved -later using the \method{get_app_data} method. -\end{methoddesc} - -\begin{methoddesc}[Connection]{set_connect_state}{} -Set the connection to work in client mode. The handshake will be handled -automatically by read/write. -\end{methoddesc} - -\begin{methoddesc}[Connection]{setblocking}{flag} -Call the \method{setblocking} method of the underlying socket. -\end{methoddesc} - -\begin{methoddesc}[Connection]{setsockopt}{level, optname, value} -Call the \method{setsockopt} method of the underlying socket. -\end{methoddesc} - -\begin{methoddesc}[Connection]{shutdown}{} -Send the shutdown message to the Connection. Returns true if the shutdown -message exchange is completed and false otherwise (in which case you call -\method{recv()} or \method{send()} when the connection becomes -readable/writeable. -\end{methoddesc} - -\begin{methoddesc}[Connection]{get_shutdown}{} -Get the shutdown state of the Connection. Returns a bitvector of either or -both of \var{SENT_SHUTDOWN} and \var{RECEIVED_SHUTDOWN}. -\end{methoddesc} - -\begin{methoddesc}[Connection]{set_shutdown}{state} -Set the shutdown state of the Connection. \var{state} is a bitvector of -either or both of \var{SENT_SHUTDOWN} and \var{RECEIVED_SHUTDOWN}. -\end{methoddesc} - -\begin{methoddesc}[Connection]{sock_shutdown}{how} -Call the \method{shutdown} method of the underlying socket. -\end{methoddesc} - -\begin{methoddesc}[Connection]{bio_shutdown}{} -If the Connection was created with a memory BIO, this method can be used to -indicate that ``end of file'' has been reached on the read end of that memory -BIO. -\end{methoddesc} - -\begin{methoddesc}[Connection]{state_string}{} -Retrieve a verbose string detailing the state of the Connection. -\end{methoddesc} - -\begin{methoddesc}[Connection]{client_random}{} -Retrieve the random value used with the client hello message. -\end{methoddesc} - -\begin{methoddesc}[Connection]{server_random}{} -Retrieve the random value used with the server hello message. -\end{methoddesc} - -\begin{methoddesc}[Connection]{master_key}{} -Retrieve the value of the master key for this session. -\end{methoddesc} - -\begin{methoddesc}[Connection]{want_read}{} -Checks if more data has to be read from the transport layer to complete an -operation. -\end{methoddesc} - -\begin{methoddesc}[Connection]{want_write}{} -Checks if there is data to write to the transport layer to complete an -operation. -\end{methoddesc} - -\begin{methoddesc}[Connection]{set_tlsext_host_name}{name} -Specify the byte string to send as the server name in the client hello message. -\versionadded{0.13} -\end{methoddesc} - -\begin{methoddesc}[Connection]{get_servername}{} -Get the value of the server name received in the client hello message. -\versionadded{0.13} -\end{methoddesc} - - - -\section{Internals \label{internals}} - -We ran into three main problems developing this: Exceptions, callbacks and -accessing socket methods. This is what this chapter is about. - -\subsection{Exceptions \label{exceptions}} - -We realized early that most of the exceptions would be raised by the I/O -functions of OpenSSL, so it felt natural to mimic OpenSSL's error code system, -translating them into Python exceptions. This naturally gives us the exceptions -\exception{SSL.ZeroReturnError}, \exception{SSL.WantReadError}, -\exception{SSL.WantWriteError}, \exception{SSL.WantX509LookupError} and -\exception{SSL.SysCallError}. - -For more information about this, see section \ref{openssl-ssl}. - - -\subsection{Callbacks \label{callbacks}} - -There are a number of problems with callbacks. First of all, OpenSSL is written -as a C library, it's not meant to have Python callbacks, so a way around that -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 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 -it (an arbitrary pointer normally). This is great! We can set our Python -function object as the real userdata and emulate userdata for the Python -function in another way. The other solution can be used if an object with an -''app_data'' system always is passed to the callback. For example, the SSL -object in OpenSSL has app_data functions and in e.g. the verification -callbacks, you can retrieve the related SSL object. What we do is to set our -wrapper \class{Connection} object as app_data for the SSL object, and we can -easily find the Python 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}} - -We quickly saw the benefit of wrapping socket methods in the -\class{SSL.Connection} class, for an easy transition into using SSL. The -problem here is that the \module{socket} module lacks a C API, and all the -methods are declared static. One approach would be to have \module{OpenSSL} as -a submodule to the \module{socket} module, placing all the code in -\file{socketmodule.c}, but this is obviously not a good solution, since you -might not want to import tonnes of extra stuff you're not going to use when -importing the \module{socket} module. The other approach is to somehow get a -pointer to the method to be called, either the C function, or a callable Python -object. This is not really a good solution either, since there's a lot of -lookups involved. - -The way it works is that you have to supply a ``\class{socket}-like'' transport -object to the \class{SSL.Connection}. The only requirement of this object is -that it has a \method{fileno()} method that returns a file descriptor that's -valid at the C level (i.e. you can use the system calls read and write). If you -want to use the \method{connect()} or \method{accept()} methods of the -\class{SSL.Connection} object, the transport object has to supply such -methods too. Apart from them, any method lookups in the \class{SSL.Connection} -object that fail are passed on to the underlying transport object. - -Future changes might be to allow Python-level transport objects, that instead -of having \method{fileno()} methods, have \method{read()} and \method{write()} -methods, so more advanced features of Python can be used. This would probably -entail some sort of OpenSSL ``BIOs'', but converting Python strings back and -forth is expensive, so this shouldn't be used unless necessary. Other nice -things would be to be able to pass in different transport objects for reading -and writing, but then the \method{fileno()} method of \class{SSL.Connection} -becomes virtually useless. Also, should the method resolution be used on the -read-transport or the write-transport? - - -\end{document} diff --git a/doc/tools/anno-api.py b/doc/tools/anno-api.py deleted file mode 100755 index 0d355d2..0000000 --- a/doc/tools/anno-api.py +++ /dev/null @@ -1,71 +0,0 @@ -#! /usr/bin/env python -"""Add reference count annotations to the Python/C API Reference.""" -__version__ = '$Revision: 1.1.1.1 $' - -import getopt -import os -import sys - -import refcounts - - -PREFIX_1 = r"\begin{cfuncdesc}{PyObject*}{" -PREFIX_2 = r"\begin{cfuncdesc}{PyVarObject*}{" - - -def main(): - rcfile = os.path.join(os.path.dirname(refcounts.__file__), os.pardir, - "api", "refcounts.dat") - outfile = "-" - opts, args = getopt.getopt(sys.argv[1:], "o:r:", ["output=", "refcounts="]) - for opt, arg in opts: - if opt in ("-o", "--output"): - outfile = arg - elif opt in ("-r", "--refcounts"): - rcfile = arg - rcdict = refcounts.load(rcfile) - if outfile == "-": - output = sys.stdout - else: - output = open(outfile, "w") - if not args: - args = ["-"] - for infile in args: - if infile == "-": - input = sys.stdin - else: - input = open(infile) - while 1: - line = input.readline() - if not line: - break - prefix = None - if line.startswith(PREFIX_1): - prefix = PREFIX_1 - elif line.startswith(PREFIX_2): - prefix = PREFIX_2 - if prefix: - s = line[len(prefix):].split('}', 1)[0] - try: - info = rcdict[s] - except KeyError: - sys.stderr.write("No refcount data for %s\n" % s) - else: - if info.result_type in ("PyObject*", "PyVarObject*"): - if info.result_refs is None: - rc = "Always \NULL{}" - else: - rc = info.result_refs and "New" or "Borrowed" - rc = rc + " reference" - line = (r"\begin{cfuncdesc}[%s]{%s}{" - % (rc, info.result_type)) \ - + line[len(prefix):] - output.write(line) - if infile != "-": - input.close() - if outfile != "-": - output.close() - - -if __name__ == "__main__": - main() diff --git a/doc/tools/buildindex.py b/doc/tools/buildindex.py deleted file mode 100755 index 5a41c0e..0000000 --- a/doc/tools/buildindex.py +++ /dev/null @@ -1,353 +0,0 @@ -#! /usr/bin/env python - -__version__ = '$Revision: 1.1.1.1 $' - -import os -import re -import string -import sys - - -class Node: - __rmjunk = re.compile("<#\d+#>") - - continuation = 0 - - def __init__(self, link, str, seqno): - self.links = [link] - self.seqno = seqno - # remove <#\d+#> left in by moving the data out of LaTeX2HTML - str = self.__rmjunk.sub('', str) - # build up the text - self.text = split_entry_text(str) - self.key = split_entry_key(str) - - def __cmp__(self, other): - """Comparison operator includes sequence number, for use with - list.sort().""" - return self.cmp_entry(other) or cmp(self.seqno, other.seqno) - - def cmp_entry(self, other): - """Comparison 'operator' that ignores sequence number.""" - c = 0 - for i in range(min(len(self.key), len(other.key))): - c = (cmp_part(self.key[i], other.key[i]) - or cmp_part(self.text[i], other.text[i])) - if c: - break - return c or cmp(self.key, other.key) or cmp(self.text, other.text) - - def __repr__(self): - return "" % (string.join(self.text, '!'), self.seqno) - - def __str__(self): - return string.join(self.key, '!') - - def dump(self): - return "%s\1%s###%s\n" \ - % (string.join(self.links, "\1"), - string.join(self.text, '!'), - self.seqno) - - -def cmp_part(s1, s2): - result = cmp(s1, s2) - if result == 0: - return 0 - l1 = string.lower(s1) - l2 = string.lower(s2) - minlen = min(len(s1), len(s2)) - if len(s1) < len(s2) and l1 == l2[:len(s1)]: - result = -1 - elif len(s2) < len(s1) and l2 == l1[:len(s2)]: - result = 1 - else: - result = cmp(l1, l2) or cmp(s1, s2) - return result - - -def split_entry(str, which): - stuff = [] - parts = string.split(str, '!') - parts = map(string.split, parts, ['@'] * len(parts)) - for entry in parts: - if len(entry) != 1: - key = entry[which] - else: - key = entry[0] - stuff.append(key) - return stuff - - -_rmtt = re.compile(r"""(.*)(.*)(.*)$""", - re.IGNORECASE) -_rmparens = re.compile(r"\(\)") - -def split_entry_key(str): - parts = split_entry(str, 1) - for i in range(len(parts)): - m = _rmtt.match(parts[i]) - if m: - parts[i] = string.join(m.group(1, 2, 3), '') - else: - parts[i] = string.lower(parts[i]) - # remove '()' from the key: - parts[i] = _rmparens.sub('', parts[i]) - return map(trim_ignored_letters, parts) - - -def split_entry_text(str): - if '<' in str: - m = _rmtt.match(str) - if m: - str = string.join(m.group(1, 2, 3), '') - return split_entry(str, 1) - - -def load(fp): - nodes = [] - rx = re.compile("(.*)\1(.*)###(.*)$") - while 1: - line = fp.readline() - if not line: - break - m = rx.match(line) - if m: - link, str, seqno = m.group(1, 2, 3) - nodes.append(Node(link, str, seqno)) - return nodes - - -def trim_ignored_letters(s): - # ignore $ to keep environment variables with the - # leading letter from the name - s = string.lower(s) - if s[0] == "$": - return s[1:] - else: - return s - -def get_first_letter(s): - return string.lower(trim_ignored_letters(s)[0]) - - -def split_letters(nodes): - letter_groups = [] - if nodes: - group = [] - append = group.append - letter = get_first_letter(nodes[0].text[0]) - letter_groups.append((letter, group)) - for node in nodes: - nletter = get_first_letter(node.text[0]) - if letter != nletter: - letter = nletter - group = [] - letter_groups.append((letter, group)) - append = group.append - append(node) - return letter_groups - - -# need a function to separate the nodes into columns... -def split_columns(nodes, columns=1): - if columns <= 1: - return [nodes] - # This is a rough height; we may have to increase to avoid breaks before - # a subitem. - colheight = len(nodes) / columns - numlong = len(nodes) % columns - if numlong: - colheight = colheight + 1 - else: - numlong = columns - cols = [] - for i in range(numlong): - start = i * colheight - end = start + colheight - cols.append(nodes[start:end]) - del nodes[:end] - colheight = colheight - 1 - try: - numshort = len(nodes) / colheight - except ZeroDivisionError: - cols = cols + (columns - len(cols)) * [[]] - else: - for i in range(numshort): - start = i * colheight - end = start + colheight - cols.append(nodes[start:end]) - # - # If items continue across columns, make sure they are marked - # as continuations so the user knows to look at the previous column. - # - for i in range(len(cols) - 1): - try: - prev = cols[i][-1] - next = cols[i + 1][0] - except IndexError: - return cols - else: - n = min(len(prev.key), len(next.key)) - for j in range(n): - if prev.key[j] != next.key[j]: - break - next.continuation = j + 1 - return cols - - -DL_LEVEL_INDENT = " " - -def format_column(nodes): - strings = ["
"] - append = strings.append - level = 0 - previous = [] - for node in nodes: - current = node.text - count = 0 - for i in range(min(len(current), len(previous))): - if previous[i] != current[i]: - break - count = i + 1 - if count > level: - append("
" * (count - level) + "\n") - level = count - elif level > count: - append("\n") - append(level * DL_LEVEL_INDENT) - append("
" * (level - count)) - level = count - # else: level == count - for i in range(count, len(current) - 1): - term = node.text[i] - level = level + 1 - if node.continuation > i: - extra = " (continued)" - else: - extra = "" - append("\n
%s%s\n
\n%s
" - % (term, extra, level * DL_LEVEL_INDENT)) - append("\n%s
%s%s" - % (level * DL_LEVEL_INDENT, node.links[0], node.text[-1])) - for link in node.links[1:]: - append(",\n%s %s[Link]" % (level * DL_LEVEL_INDENT, link)) - previous = current - append("\n") - append("
" * (level + 1)) - return string.join(strings, '') - - -def format_nodes(nodes, columns=1): - strings = [] - append = strings.append - if columns > 1: - colnos = range(columns) - colheight = len(nodes) / columns - if len(nodes) % columns: - colheight = colheight + 1 - colwidth = 100 / columns - append('') - for col in split_columns(nodes, columns): - append('") - append("\n
\n' % colwidth) - append(format_column(col)) - append("\n
") - else: - append(format_column(nodes)) - append("\n

\n") - return string.join(strings, '') - - -def format_letter(letter): - if letter == '.': - lettername = ". (dot)" - elif letter == '_': - lettername = "_ (underscore)" - else: - lettername = string.upper(letter) - return "\n


\n

%s

\n\n" \ - % (letter, lettername) - - -def format_html_letters(nodes, columns=1): - letter_groups = split_letters(nodes) - items = [] - for letter, nodes in letter_groups: - s = "%s" % (letter, letter) - items.append(s) - s = ["
\n%s
\n" % string.join(items, " |\n")] - for letter, nodes in letter_groups: - s.append(format_letter(letter)) - s.append(format_nodes(nodes, columns)) - return string.join(s, '') - -def format_html(nodes, columns): - return format_nodes(nodes, columns) - - -def collapse(nodes): - """Collapse sequences of nodes with matching keys into a single node. - Destructive.""" - if len(nodes) < 2: - return - prev = nodes[0] - i = 1 - while i < len(nodes): - node = nodes[i] - if not node.cmp_entry(prev): - prev.links.append(node.links[0]) - del nodes[i] - else: - i = i + 1 - prev = node - - -def dump(nodes, fp): - for node in nodes: - fp.write(node.dump()) - - -def process_nodes(nodes, columns, letters): - nodes.sort() - collapse(nodes) - if letters: - return format_html_letters(nodes, columns) - else: - return format_html(nodes, columns) - - -def main(): - import getopt - ifn = "-" - ofn = "-" - columns = 1 - letters = 0 - opts, args = getopt.getopt(sys.argv[1:], "c:lo:", - ["columns=", "letters", "output="]) - for opt, val in opts: - if opt in ("-o", "--output"): - ofn = val - elif opt in ("-c", "--columns"): - columns = string.atoi(val) - elif opt in ("-l", "--letters"): - letters = 1 - if not args: - args = [ifn] - nodes = [] - for fn in args: - nodes = nodes + load(open(fn)) - num_nodes = len(nodes) - html = process_nodes(nodes, columns, letters) - program = os.path.basename(sys.argv[0]) - if ofn == "-": - sys.stdout.write(html) - sys.stderr.write("\n%s: %d index nodes" % (program, num_nodes)) - else: - open(ofn, "w").write(html) - print - print "%s: %d index nodes" % (program, num_nodes) - - -if __name__ == "__main__": - main() diff --git a/doc/tools/checkargs.pm b/doc/tools/checkargs.pm deleted file mode 100644 index de52f69..0000000 --- a/doc/tools/checkargs.pm +++ /dev/null @@ -1,112 +0,0 @@ -#!/uns/bin/perl - -package checkargs; -require 5.004; # uses "for my $var" -require Exporter; -@ISA = qw(Exporter); -@EXPORT = qw(check_args check_args_range check_args_at_least); -use strict; -use Carp; - -=head1 NAME - -checkargs -- Provide rudimentary argument checking for perl5 functions - -=head1 SYNOPSIS - - check_args(cArgsExpected, @_) - check_args_range(cArgsMin, cArgsMax, @_) - check_args_at_least(cArgsMin, @_) -where "@_" should be supplied literally. - -=head1 DESCRIPTION - -As the first line of user-written subroutine foo, do one of the following: - - my ($arg1, $arg2) = check_args(2, @_); - my ($arg1, @rest) = check_args_range(1, 4, @_); - my ($arg1, @rest) = check_args_at_least(1, @_); - my @args = check_args_at_least(0, @_); - -These functions may also be called for side effect (put a call to one -of the functions near the beginning of the subroutine), but using the -argument checkers to set the argument list is the recommended usage. - -The number of arguments and their definedness are checked; if the wrong -number are received, the program exits with an error message. - -=head1 AUTHOR - -Michael D. Ernst > - -=cut - -## Need to check that use of caller(1) really gives desired results. -## Need to give input chunk information. -## Is this obviated by Perl 5.003's declarations? Not entirely, I think. - -sub check_args ( $@ ) -{ - my ($num_formals, @args) = @_; - my ($pack, $file_arg, $line_arg, $subname, $hasargs, $wantarr) = caller(1); - if (@_ < 1) { croak "check_args needs at least 7 args, got ", scalar(@_), ": @_\n "; } - if ((!wantarray) && ($num_formals != 0)) - { croak "check_args called in scalar context"; } - # Can't use croak below here: it would only go out to caller, not its caller - my $num_actuals = @args; - if ($num_actuals != $num_formals) - { die "$file_arg:$line_arg: function $subname expected $num_formals argument", - (($num_formals == 1) ? "" : "s"), - ", got $num_actuals", - (($num_actuals == 0) ? "" : ": @args"), - "\n"; } - for my $index (0..$#args) - { if (!defined($args[$index])) - { die "$file_arg:$line_arg: function $subname undefined argument ", $index+1, ": @args[0..$index-1]\n"; } } - return @args; -} - -sub check_args_range ( $$@ ) -{ - my ($min_formals, $max_formals, @args) = @_; - my ($pack, $file_arg, $line_arg, $subname, $hasargs, $wantarr) = caller(1); - if (@_ < 2) { croak "check_args_range needs at least 8 args, got ", scalar(@_), ": @_"; } - if ((!wantarray) && ($max_formals != 0) && ($min_formals !=0) ) - { croak "check_args_range called in scalar context"; } - # Can't use croak below here: it would only go out to caller, not its caller - my $num_actuals = @args; - if (($num_actuals < $min_formals) || ($num_actuals > $max_formals)) - { die "$file_arg:$line_arg: function $subname expected $min_formals-$max_formals arguments, got $num_actuals", - ($num_actuals == 0) ? "" : ": @args", "\n"; } - for my $index (0..$#args) - { if (!defined($args[$index])) - { die "$file_arg:$line_arg: function $subname undefined argument ", $index+1, ": @args[0..$index-1]\n"; } } - return @args; -} - -sub check_args_at_least ( $@ ) -{ - my ($min_formals, @args) = @_; - my ($pack, $file_arg, $line_arg, $subname, $hasargs, $wantarr) = caller(1); - # Don't do this, because we want every sub to start with a call to check_args* - # if ($min_formals == 0) - # { die "Isn't it pointless to check for at least zero args to $subname?\n"; } - if (scalar(@_) < 1) - { croak "check_args_at_least needs at least 1 arg, got ", scalar(@_), ": @_"; } - if ((!wantarray) && ($min_formals != 0)) - { croak "check_args_at_least called in scalar context"; } - # Can't use croak below here: it would only go out to caller, not its caller - my $num_actuals = @args; - if ($num_actuals < $min_formals) - { die "$file_arg:$line_arg: function $subname expected at least $min_formals argument", - ($min_formals == 1) ? "" : "s", - ", got $num_actuals", - ($num_actuals == 0) ? "" : ": @args", "\n"; } - for my $index (0..$#args) - { if (!defined($args[$index])) - { warn "$file_arg:$line_arg: function $subname undefined argument ", $index+1, ": @args[0..$index-1]\n"; last; } } - return @args; -} - -1; # successful import -__END__ diff --git a/doc/tools/cklatex b/doc/tools/cklatex deleted file mode 100755 index 396e914..0000000 --- a/doc/tools/cklatex +++ /dev/null @@ -1,26 +0,0 @@ -#! /bin/sh -# -*- ksh -*- - -# This script *helps* locate lines of normal content that end in '}'; -# this is useful since LaTeX2HTML (at least the old version that we -# use) breaks on many lines that end that way. -# -# Usage: cklatex files... | less -# -# *Read* the output looking for suspicious lines! - -grep -n "[^ ]}\$" $@ | \ - grep -v '\\begin{' | \ - grep -v '\\end{' | \ - grep -v '\\input{' | \ - grep -v '\\documentclass{' | \ - grep -v '\\title{' | \ - grep -v '\\chapter{' | \ - grep -v '\\chapter\*{' | \ - grep -v '\\section{' | \ - grep -v '\\subsection{' | \ - grep -v '\\subsubsection{' | \ - grep -v '\\sectionauthor{' | \ - grep -v '\\moduleauthor{' - -exit $? diff --git a/doc/tools/custlib.py b/doc/tools/custlib.py deleted file mode 100644 index 9958451..0000000 --- a/doc/tools/custlib.py +++ /dev/null @@ -1,73 +0,0 @@ -# Generate custlib.tex, which is a site-specific library document. - -# Phase I: list all the things that can be imported - -import glob, os, sys, string -modules={} - -for modname in sys.builtin_module_names: - modules[modname]=modname - -for dir in sys.path: - # Look for *.py files - filelist=glob.glob(os.path.join(dir, '*.py')) - for file in filelist: - path, file = os.path.split(file) - base, ext=os.path.splitext(file) - modules[string.lower(base)]=base - - # Look for shared library files - filelist=(glob.glob(os.path.join(dir, '*.so')) + - glob.glob(os.path.join(dir, '*.sl')) + - glob.glob(os.path.join(dir, '*.o')) ) - for file in filelist: - path, file = os.path.split(file) - base, ext=os.path.splitext(file) - if base[-6:]=='module': base=base[:-6] - modules[string.lower(base)]=base - -# Minor oddity: the types module is documented in libtypes2.tex -if modules.has_key('types'): - del modules['types'] ; modules['types2']=None - -# Phase II: find all documentation files (lib*.tex) -# and eliminate modules that don't have one. - -docs={} -filelist=glob.glob('lib*.tex') -for file in filelist: - modname=file[3:-4] - docs[modname]=modname - -mlist=modules.keys() -mlist=filter(lambda x, docs=docs: docs.has_key(x), mlist) -mlist.sort() -mlist=map(lambda x, docs=docs: docs[x], mlist) - -modules=mlist - -# Phase III: write custlib.tex - -# Write the boilerplate -# XXX should be fancied up. -print """\documentstyle[twoside,11pt,myformat]{report} -\\title{Python Library Reference} -\\input{boilerplate} -\\makeindex % tell \\index to actually write the .idx file -\\begin{document} -\\pagenumbering{roman} -\\maketitle -\\input{copyright} -\\begin{abstract} -\\noindent This is a customized version of the Python Library Reference. -\\end{abstract} -\\pagebreak -{\\parskip = 0mm \\tableofcontents} -\\pagebreak\\pagenumbering{arabic}""" - -for modname in mlist: - print "\\input{lib%s}" % (modname,) - -# Write the end -print """\\input{custlib.ind} % Index -\\end{document}""" diff --git a/doc/tools/cvsinfo.py b/doc/tools/cvsinfo.py deleted file mode 100644 index 58a32c2..0000000 --- a/doc/tools/cvsinfo.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Utility class and function to get information about the CVS repository -based on checked-out files. -""" - -import os - - -def get_repository_list(paths): - d = {} - for name in paths: - if os.path.isfile(name): - dir = os.path.dirname(name) - else: - dir = name - rootfile = os.path.join(name, "CVS", "Root") - root = open(rootfile).readline().strip() - if not d.has_key(root): - d[root] = RepositoryInfo(dir), [name] - else: - d[root][1].append(name) - return d.values() - - -class RepositoryInfo: - """Record holding information about the repository we want to talk to.""" - cvsroot_path = None - branch = None - - # type is '', ':ext', or ':pserver:' - type = "" - - def __init__(self, dir=None): - if dir is None: - dir = os.getcwd() - dir = os.path.join(dir, "CVS") - root = open(os.path.join(dir, "Root")).readline().strip() - if root.startswith(":pserver:"): - self.type = ":pserver:" - root = root[len(":pserver:"):] - elif ":" in root: - if root.startswith(":ext:"): - root = root[len(":ext:"):] - self.type = ":ext:" - self.repository = root - if ":" in root: - host, path = root.split(":", 1) - self.cvsroot_path = path - else: - self.cvsroot_path = root - fn = os.path.join(dir, "Tag") - if os.path.isfile(fn): - self.branch = open(fn).readline().strip()[1:] - - def get_cvsroot(self): - return self.type + self.repository - - _repository_dir_cache = {} - - def get_repository_file(self, path): - filename = os.path.abspath(path) - if os.path.isdir(path): - dir = path - join = 0 - else: - dir = os.path.dirname(path) - join = 1 - try: - repodir = self._repository_dir_cache[dir] - except KeyError: - repofn = os.path.join(dir, "CVS", "Repository") - repodir = open(repofn).readline().strip() - repodir = os.path.join(self.cvsroot_path, repodir) - self._repository_dir_cache[dir] = repodir - if join: - fn = os.path.join(repodir, os.path.basename(path)) - else: - fn = repodir - return fn[len(self.cvsroot_path)+1:] - - def __repr__(self): - return "" % `self.get_cvsroot()` diff --git a/doc/tools/findacks b/doc/tools/findacks deleted file mode 100755 index c13b00f..0000000 --- a/doc/tools/findacks +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/env python -"""Script to locate email addresses in the CVS logs.""" -__version__ = '$Revision: 1.1.1.1 $' - -import os -import re -import sys -import UserDict - -import cvsinfo - - -class Acknowledgements(UserDict.UserDict): - def add(self, email, name, path): - d = self.data - d.setdefault(email, {})[path] = name - - -def open_cvs_log(info, paths=None): - cvsroot = info.get_cvsroot() - cmd = "cvs -q -d%s log " % cvsroot - if paths: - cmd += " ".join(paths) - return os.popen(cmd, "r") - - -email_rx = re.compile("<([a-z][-a-z0-9._]*@[-a-z0-9.]+)>", re.IGNORECASE) - -def find_acks(f, acks): - prev = '' - filename = None - MAGIC_WORDS = ('van', 'von') - while 1: - line = f.readline() - if not line: - break - if line.startswith("Working file: "): - filename = line.split(None, 2)[2].strip() - prev = line - continue - m = email_rx.search(line) - if m: - words = prev.split() + line[:m.start()].split() - L = [] - while words \ - and (words[-1][0].isupper() or words[-1] in MAGIC_WORDS): - L.insert(0, words.pop()) - name = " ".join(L) - email = m.group(1).lower() - acks.add(email, name, filename) - prev = line - - -def load_cvs_log_acks(acks, args): - repolist = cvsinfo.get_repository_list(args or [""]) - for info, paths in repolist: - print >>sys.stderr, "Repository:", info.get_cvsroot() - f = open_cvs_log(info, paths) - find_acks(f, acks) - f.close() - - -def load_tex_source_acks(acks, args): - for path in args: - path = path or os.curdir - if os.path.isfile(path): - read_acks_from_tex_file(acks, path) - else: - read_acks_from_tex_dir(acks, path) - - -def read_acks_from_tex_file(acks, path): - f = open(path) - while 1: - line = f.readline() - if not line: - break - if line.startswith(r"\sectionauthor{"): - line = line[len(r"\sectionauthor"):] - name, line = extract_tex_group(line) - email, line = extract_tex_group(line) - acks.add(email, name, path) - - -def read_acks_from_tex_dir(acks, path): - stack = [path] - while stack: - p = stack.pop() - for n in os.listdir(p): - n = os.path.join(p, n) - if os.path.isdir(n): - stack.insert(0, n) - elif os.path.normpath(n).endswith(".tex"): - read_acks_from_tex_file(acks, n) - - -def extract_tex_group(s): - c = 0 - for i in range(len(s)): - if s[i] == '{': - c += 1 - elif s[i] == '}': - c -= 1 - if c == 0: - return s[1:i], s[i+1:] - - -def print_acks(acks): - first = 1 - for email, D in acks.items(): - if first: - first = 0 - else: - print - L = D.items() - L.sort() - prefname = L[0][1] - for file, name in L[1:]: - if name != prefname: - prefname = "" - break - if prefname: - print prefname, "<%s>:" % email - else: - print email + ":" - for file, name in L: - if name == prefname: - print " " + file - else: - print " %s (as %s)" % (file, name) - - -def print_ack_names(acks): - names = [] - for email, D in acks.items(): - L = D.items() - L.sort() - prefname = L[0][1] - for file, name in L[1:]: - prefname = prefname or name - names.append(prefname or email) - def f(s1, s2): - s1 = s1.lower() - s2 = s2.lower() - return cmp((s1.split()[-1], s1), - (s2.split()[-1], s2)) - names.sort(f) - for name in names: - print name - - -def main(): - args = sys.argv[1:] - acks = Acknowledgements() - load_cvs_log_acks(acks, args) - load_tex_source_acks(acks, args) - print_ack_names(acks) - - -if __name__ == "__main__": - main() diff --git a/doc/tools/findmodrefs b/doc/tools/findmodrefs deleted file mode 100755 index 8c5f93f..0000000 --- a/doc/tools/findmodrefs +++ /dev/null @@ -1,63 +0,0 @@ -#! /usr/bin/env python -# -*- Python -*- - -import fileinput -import getopt -import glob -import os -import re -import sys - - -declare_rx = re.compile( - r"\\declaremodule(?:\[[a-zA-Z0-9]*\]*)?{[a-zA-Z_0-9]+}{([a-zA-Z_0-9]+)}") - -module_rx = re.compile(r"\\module{([a-zA-Z_0-9]+)}") - -def main(): - try: - just_list = 0 - print_lineno = 0 - opts, args = getopt.getopt(sys.argv[1:], "ln", ["list", "number"]) - for opt, arg in opts: - if opt in ("-l", "--list"): - just_list = 1 - elif opt in ("-n", "--number"): - print_lineno = 1 - files = args - if not files: - files = glob.glob("*.tex") - files.sort() - modulename = None - for line in fileinput.input(files): - if line[:9] == r"\section{": - modulename = None - continue - if line[:16] == r"\modulesynopsys{": - continue - m = declare_rx.match(line) - if m: - modulename = m.group(1) - continue - if not modulename: - continue - m = module_rx.search(line) - if m: - name = m.group(1) - if name != modulename: - filename = fileinput.filename() - if just_list: - print filename - fileinput.nextfile() - modulename = None - elif print_lineno: - print "%s(%d):%s" \ - % (filename, fileinput.filelineno(), line[:-1]) - else: - print "%s:%s" % (filename, line[:-1]) - except KeyboardInterrupt: - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/doc/tools/fix_hack b/doc/tools/fix_hack deleted file mode 100755 index 8dad111..0000000 --- a/doc/tools/fix_hack +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -sed -e 's/{\\ptt[ ]*\\char[ ]*'"'"'137}/_/g' <"$1" > "@$1" && mv "@$1" $1 diff --git a/doc/tools/fix_libaux.sed b/doc/tools/fix_libaux.sed deleted file mode 100755 index fb33cc5..0000000 --- a/doc/tools/fix_libaux.sed +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/sed -f -s/{\\tt \\hackscore {}\\hackscore {}/\\sectcode{__/ -s/\\hackscore {}\\hackscore {}/__/ diff --git a/doc/tools/fixinfo.el b/doc/tools/fixinfo.el deleted file mode 100644 index 267a7e3..0000000 --- a/doc/tools/fixinfo.el +++ /dev/null @@ -1,15 +0,0 @@ -(defun fix-python-texinfo () - (goto-char (point-min)) - (replace-regexp "\\(@setfilename \\)\\([-a-z]*\\)$" - "\\1python-\\2.info") - (replace-string "@node Front Matter\n@chapter Abstract\n" - "@node Abstract\n@section Abstract\n") - (mark-whole-buffer) - (texinfo-master-menu 'update-all-nodes) - (save-buffer) - ) ;; fix-python-texinfo - -;; now really do it: -(find-file (car command-line-args-left)) -(fix-python-texinfo) -(kill-emacs) diff --git a/doc/tools/getpagecounts b/doc/tools/getpagecounts deleted file mode 100755 index 179ced1..0000000 --- a/doc/tools/getpagecounts +++ /dev/null @@ -1,88 +0,0 @@ -#! /usr/bin/env python -# -*- Python -*- - -"""Generate a page count report of the PostScript version of the manuals.""" - -__version__ = '$Revision: 1.1.1.1 $' - - -class PageCounter: - def __init__(self): - self.doclist = [] - self.total = 0 - self.title_width = 0 - - def add_document(self, prefix, title): - count = count_pages(prefix + ".ps") - self.doclist.append((title, prefix, count)) - self.title_width = max(self.title_width, len(title)) - self.total = self.total + count - - def dump(self): - fmt = "%%-%ds (%%s.ps, %%d pages)" % self.title_width - for item in self.doclist: - print fmt % item - print - print " Total page count: %d" % self.total - - def run(self): - for prefix, title in [ - ("api", "Python/C API"), - ("ext", "Extending and Embedding the Python Interpreter"), - ("lib", "Python Library Reference"), - ("mac", "Macintosh Module Reference"), - ("ref", "Python Reference Manual"), - ("tut", "Python Tutorial"), - ("doc", "Documenting Python"), - ("inst", "Installing Python Modules"), - ("dist", "Distributing Python Modules"), - ]: - self.add_document(prefix, title) - print self.PREFIX - self.dump() - print self.SUFFIX - - PREFIX = """\ -This is the PostScript version of the standard Python documentation. -If you plan to print this, be aware that some of the documents are -long. It is formatted for printing on two-sided paper; if you do plan -to print this, *please* print two-sided if you have a printer capable -of it! To locate published copies of the larger manuals, or other -Python reference material, consult the PSA Online Bookstore at: - - http://www.python.org/psa/bookstore/ - -The following manuals are included: -""" - SUFFIX = """\ - - -If you have any questions, comments, or suggestions regarding these -documents, please send them via email to python-docs@python.org. - -If you would like to support the development and maintenance of -documentation for Python, please consider joining the Python Software -Activity (PSA; see http://www.python.org/psa/), or urging your -organization to join the PSA or the Python Consortium (see -http://www.python.org/consortium/). -""" - -def count_pages(filename): - fp = open(filename) - count = 0 - while 1: - lines = fp.readlines(1024*40) - if not lines: - break - for line in lines: - if line[:7] == "%%Page:": - count = count + 1 - fp.close() - return count - - -def main(): - PageCounter().run() - -if __name__ == "__main__": - main() diff --git a/doc/tools/html/about.dat b/doc/tools/html/about.dat deleted file mode 100644 index e6f8b55..0000000 --- a/doc/tools/html/about.dat +++ /dev/null @@ -1,24 +0,0 @@ -

This document was generated using the - LaTeX2HTML translator. -

- -

- LaTeX2HTML is Copyright © - 1993, 1994, 1995, 1996, 1997, Nikos - Drakos, Computer Based Learning Unit, University of - Leeds, and Copyright © 1997, 1998, Ross - Moore, Mathematics Department, Macquarie University, - Sydney. -

- -

The application of - LaTeX2HTML to the Python - documentation has been heavily tailored by Fred L. Drake, - Jr. Original navigation icons were contributed by Christopher - Petrilli. -

diff --git a/doc/tools/html/about.html b/doc/tools/html/about.html deleted file mode 100644 index 3203faf..0000000 --- a/doc/tools/html/about.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - About the Python Documentation - - - - - - -
- -

About the Python Documentation

- -

The Python documentation was originally written by Guido van - Rossum, but has increasingly become a community effort over the - past several years. This growing collection of documents is - available in several formats, including typeset versions in PDF - and PostScript for printing, from the Python Web site. - -

A list of contributors is available. - -

Comments and Questions

- -

General comments and questions regarding this document should - be sent by email to python-docs@python.org. If you find specific errors in - this document, please report the bug at the Python Bug - Tracker at SourceForge. -

- -

Questions regarding how to use the information in this - document should be sent to the Python news group, comp.lang.python, or the Python mailing list (which is gated to the newsgroup and - carries the same content). -

- -

For any of these channels, please be sure not to send HTML email. - Thanks. -

- -
- - diff --git a/doc/tools/html/icons/blank.gif b/doc/tools/html/icons/blank.gif deleted file mode 100644 index 2e31f4ed50ee9a3dc57f62d88392e7928d262c07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1958 zcmd^;Id0QY5XQ&xnu0s{08ottNHkO+&OieNQ*j77iu8!N21s0jxd3hAE%Cm@juXej z&#$6Ofg-WI%J0q0H{as_%U3U+K6%@gwtSSssZ=UQM@Q9awN|Uu>-9#X(QGzbt=94J zF;x{Y5f=%OEO%8g6;}zBEM+w@6E_KyEZyp2F76U8S%x*lLOdiwvS4Y7rFcrDWPw%z ziBWnfayuMTb=6SKvY5M>x@nkZ;X&D4-8EdZ(4yNyJv2hIuw>X$JvCCZkVXWop$RXD zu$fh01I9G%)Op~zS(s%}$YSp97H(Mt!-KMiMOYU7(4yPZA}xy@xP?9VfdME+BAO5Z zYiPm?B5Vd4Y`~bNojMPkaL-~T3R%oO!m~JwV0ciD^ejdb3prpsPGc=zVjgZ`4}M?( zijfFuuwV^MctM2CK!Xh!)3j6Pfs-XOtVAJ;xo3%x{0W?diIfZpjg*Lm955cIu@*0} z1C|Wq2L_-RiI4^h*3g6(MA!^8*nlxjJ9Qp7r>Cc9XJ?&Gr`zrJdcA(XKNt+o&(AL| zE{4P5Xfzs+$CJrqI-SmDv-y0!SS*&ygsB{-Ckc`-`w2ncDvi# z+x>nI^25UyaQGIUw^F{o`~Ku>LWeEA=LKALIe|!wxvrN0~ja~uAbC*@nW(^ z6HX>_CjJ4Q_HN>lukuZyd>2@M-AUSJc4pqZ_a^;3Ix^JJ-q|i99ogZ`D}E!5PdLbT zX6efxk)Vve7#}1J1OiP>O~GKWxw$zM3bnMfw6?Z}!{N5JHmXWQix?3r;(V@@XeCC8 zRpNYEY0+AY7OTbiZe>IpF-EKr=ZBRQZN*rzR-DIDPP7x_#5!>vS_w#u(o2!75n3su zlvT?4#pGHmqm|Xlc^;ItQN}21l=HOc)>av-tX0mlWLP_8oU%?iPZ|-hh9|?r7Lz-FLhjGOxiBZ?h8&G6aTe}?@i>jO zc!?dbWEej%0L4gzG+3~PCcGfRW}v|ajA`1b^FXYwuCA@Et*@_dY;0_9ZdNLlt*x!? z?d_eNo!#Bty}iBt{r!W3gTuqaYPEWFbaZ@tT&vYiPEJlwPtVTI&d<*;E-o%FFR!ky z>h=2d_4Uoo&F$?i$OHYIe;faS{PUrVd)6k?OI;$r znkJ%wAC+Y~2FinP%40LS{2@16l)lH0o@A$Tlf{?0$x`86BoU2Adn4&|Ppr2m_B2wM zEzOk|`=%D>%B4cII2WCme?K!_juvL7zWsi<0$i7eD@{)4%JbmGa)neXR!pSQI0C0IOr??&Iv!8t68U27&i6V~cNY($TQaJwElDoXb{P z{iKTTfktP#y~XC0jUJp#lZSML7^`tb6&=(bvdv16}%BLs@B+A z<>Fo^)a#KlQRA*qPm^bZhl&`J&_aPqkqo6nizOUfc$PGUJ}H>O;jEeB*fqn|dMO8! zrs_UrMq4YPw9CrO%&LwxjNF18D$I3^j?G$mjZSThY=S(D8Tp#iV;HS@RXc3sEVQ@; zix|u2GxjW6Be+hTuaqq?ZXLJ41ncEWOc6>OSQ6GZED$(c#b3i5(CxI2U4WC7>!J#q zKr~mE8p{M#*464y?Owimt;fVYla;rNrHhN5mzRZ$m+K4Tw;x|>oe~<_m>GGN*%Tb{ z^3*ZpS)XVu;yquLCFGH5iiE=A71_)dg*pK(DU58~cGgxo1rJhA^>SBrR78A=Vq({F yzjww`;hH^D;28~#=PK%FS+o}ac$3N2$Xvl$m3PG4WR({a-;E8oZz-`bSOWl(s*7p> diff --git a/doc/tools/html/icons/contents.png b/doc/tools/html/icons/contents.png deleted file mode 100644 index 3429be0c1d45d9b1f0816da78621c029f512de1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 649 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy2?0JKuK)l4pLyng|DmsKJKxl- ze^k8eX6Bsp@so~*b?))0-{f4j(kgd>LGm=U$R62%W?{EVF53bI<20adB0;ac!CIhF z#w2fd7oqAsIUu&Ir;B4q#jUfG?oK+az|q>>#C1&m-v3l_L(kpjZ++9#jb~Q4+3_XR z9B6<3v7g}&LrNuo%Gvh}Ijg4ntJ`I`J0-45WM0x7CTzi@Ju7TZTjSpMLMyILf7xB8 zzpLlj9fsp zeuu2_;)6`NKZ_-)w2oSBzJ<5*SaeU1+jXReym>iyYq)M&223n3t=3{GA}JF@a0-?ty6= z4|59K_IAJPaC-WhpW+ApmRY3ca0~EUaO{{dzu`0k-;rwzOJ=t`j&$K^?=t zlYFakZp7OHL&~QrB%&lZKdq!Zu_%>+q2gBYg@9{c&iMNHfA#e_?{hB9;G*#*uns|DeZt?IkVPWWX<(OZvdC7C2 sc5#sQfW&mLlZ?$)8JJx%yI{t^z%`5Ur+fKfQJ@kAPgg&ebxsLQ0It{kg8%>k diff --git a/doc/tools/html/icons/index.gif b/doc/tools/html/icons/index.gif deleted file mode 100644 index 32eecfb4fd4bca98f6601e677fa0e667c3a1efe3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 289 zcmZ?wbhEHbRA5kGIP#z2|Ns9p&;0K{^tEm0o0|2HikIEYoO3>Y($TQaJwElDoXb{P z{iKTTfktP#y~XC0jUJp#lX5OL7^`tb6&=(bvdu+v3=SX7;-^cMjqsd~%GA9>CT~(zvt(6O}bg@iq3s31#ImX!PTkxTXwIj7u zuu$NF+mpyDJ&b7+7!9W=aq$;v&*J5q z+r!Bh!kW~b&dV*pn6%1`o1b~Z)}XD7+$;+i`8kYcZalJe4dd!&Zf5_Q%c_bD)&L0t BTp0iW diff --git a/doc/tools/html/icons/index.png b/doc/tools/html/icons/index.png deleted file mode 100644 index cd918afe76ca2913a4de7ac7663211878b0c263f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 529 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy2?0JKuK)l4pLyng|DmsKJKxl- ze^k8eX6Bsp@so~*b?))0-{f4j(kgd>LGm=U$R62%W?{EVF53bI<20adB0;ac!CIhF z#w2fd7oqAsIUsFIJzX3_DsFAPbdm3{0tf4bMXs#u)BgXzdFjnr%UL?B*Ux+KwT(CL z+_(CGTf7E`B|d1|N*Rg2lJF1kHncD~*Ko{GLECVG&NY_%=9 z{C8Qq4_rQDCxf4A&_I-XbVGq#3K2;$RCBgY=CFO}l zsSFGiw~8+WT>Emy*T?^>ug`g(b72M-jV~DoZP;?f+~BIYL5PRmDIL9)B`4PE=$-Vr z=B<0pTT}PMdF_kd=gzY-eDM})_;UA19MD{2u(_G(d5I;Z5QB|P{fzyM{7ijK%?(U_ zOiT@p{I+=*d;1vs7`C(C@5(ZCKKbLh*2~7a755D*S diff --git a/doc/tools/html/icons/modules.gif b/doc/tools/html/icons/modules.gif deleted file mode 100644 index f5860b6bb9c4ce21e25ca74b0fb90faaaf231993..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 385 zcmZ?wbhEHbRA5kGIP#z2|Ns9p&;0K{^tEm0o0|2HikIEYoO3>Y($TQaJwElDoXb{P z{iKTTfktP#y~XC0jUJp#lZSML7^`tb6&=(bvdv173iEV$-Q4T zQKc)wfV=U<1&+oZeq4N_(o)_}U;aU1zf@uM5%DzVY0=106wfVe;%z`}1(`G1DaWJ*(c(i+zDK$Cj z^Moj`3SCwmZ=u4vY3n8yH_mu&o=`4ce|8p@LutIck+YRq1(*d`IXGBm@G|i-bFx}l zDNRZ5x8vvKvv-;@g^lCp?V@{p>rQjJvfg{nX!ZJLkpS-zZdI#-i?7+9-Q&ys#nDy3 w&^OzT`5Kp=zzM&F4LO^OLLGm=U$R62%W?{EVF53bI<20adB0;ac!CIhF z#w2fd7oqAsIUsGXJY5_^DsH{Kbg<}%0tedzR@WmO6TjErOqz6e*=(Qco5{sL_|KUi z`tyvjzNWwa1E0qd*Ke$h5l^47ePi1*Lp&q*YYFS4R?A6IY+q+fpXp4$#$@n@>w@pL zH5)yBb~ilI&fDp~yXn~?`hzbNux0)FaWKvLDLKtM7)-DE-S)yE@JP zqv`*DVqYF!Kf^RB@g?Uhu?KT3kMCP`rk360(t@aBc2{2~6_L5_!foskS63vV}1V#`3H2Y)7~&VTJeCz+m2D>7h8tgf@=yhm)_9gPYwR#5E##Q zl4sT2ou>~2eeF{f5>XPIpH@ zSIiBrnj3_8=$+EhTUl~qt&ZMFpKIQ_*Ss}#Pn_4j=zZ=yE5jFWp@uJakHi7ZH3plT znVy$eQVKEHdy|2wx3RaOk%@<)k(rsdmyfZpsfm%nHWSY+2IgB07#O-=aOh>eQs4n< s7YAt%NK6Mi$=Ga_f!QUq3uX)qT(cN|x|bgo1u9|iboFyt=akR{0EAiRtN;K2 diff --git a/doc/tools/html/icons/next.gif b/doc/tools/html/icons/next.gif deleted file mode 100644 index 5dcaff8b2429e50aad5de34344459cebef9e0957..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 253 zcmZ?wbhEHbRA5kGIP#z2|Ns9p&;0K{^tEm0o0|2HikIEYoO3>Y($TQaJwElDoXb{P zg2lA{-J z-=104^TCI&GE5IMJ{UMjoDw|98R+85{_3GrKtS>JHenB~pz|5ZMN{&Yi!WMP{Z5Bv z)0aPH6Hc6$Pq1Q2%5rpKPP60nbYk&!EAV3Px8n$`b>n9Xw3*px?BbW*&Sh@K)f2O7 O^_sQo)~hNqSOWl>6;&qy diff --git a/doc/tools/html/icons/next.png b/doc/tools/html/icons/next.png deleted file mode 100644 index cfe5e51ca619b357fc22d618fa2cb132036da31f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 511 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy2?0JKuK)l4pLyng|DmsKJKxl- ze^k8eX6Bsp@so~*b?))0-{f4j(kgd>LGm=U$R62%W-i+T2IDlKR^mbIJX?@mj7i?^ zE`pD?+U^2!CVRR#hE&`-JNcl{Ap;JVWQUcVoIn4spW0L=b9UR~wbf63)8v=FESgZ_ z<}j=MBX{YVz$S-xeqVU3Cs=(vpn9`0a6>DLV#H>agBAR_!G;P^{>fTwJ##~tEBGa@ ze_3bKe}lW+CX7wYQpJkVmuvmBgNq{KnG&uFeP*qF_hOBCi9zXVd)9q@PuUkd`W|8T z?D?OO|E6o##^(5>XPIpH@SIiBrnj3_8=$+EhTUl~qt&ZMFpKIQ_*Ss}#Pn_4j=zZ=yE5jFWp@uJa zkHi7ZH3plTnVy$eQVKEH*vQy$^A;Zyb7ON8Z$m>9BQrlAW5ewRranf71|B}fEDSYy z>=#b%J8=Z4T^yu6ATb^6BxAEx240W+V6sUy3)78&qol`;+0AXLa A&;S4c diff --git a/doc/tools/html/icons/previous.gif b/doc/tools/html/icons/previous.gif deleted file mode 100644 index de1da16023d2b5c20e6f1c8546c04e434136ac6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 252 zcmZ?wbhEHbRA5kGIP#z2|Ns9p&;0K{^tEm0o0|2HikIEYoO3>Y($TQaJwElDoXb{P z{iKTTfktP#y~XC0jUJp#lYH_pwO3+IWJ??x}4Yh3Ut`rlkXq( z(F?rq%re*Gz=y9gjt>tc_L#67Vqm-!=z}!6qV*&EihG9 zIC1R#*_Ia#?|<_sbJf*)D!C>zhnM8L2ZS@1*%o)PWN>wvWF&MoCAS9h#`UH0n43+X Qv2xYwHEY*tD>7IE016OT9smFU diff --git a/doc/tools/html/icons/previous.png b/doc/tools/html/icons/previous.png deleted file mode 100644 index 497def42a0cf47e012bcd69599c2d3c405c3bca4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 511 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy2?0JKuK)l4pLyng|DmsKJKxl- ze^k8eX6Bsp@so~*b?))0-{f4j(kgd>LGm=U$R62%W?{EVF53bI<20adB0;ac!CIhF z#w2fd7oqAsIUsG5JzX3_DsEk!wRyY zCEWY>_i=7wnwQ?Thv7!lk3Q3b_hn3_6cyBDCqA^@uz$wKgFRYIje)6~-oBPwCCtL8 zRn{-bS5|{;$vbCgx;QBrpAU_J-m#4jD3wv4E??ROxPGE zh;n@Sv(s`4P`fxtdq843*h$7_s|?I8nO!hrVBng?_|v`ouqaRogQu&X%Q~loCID!* Bwsrsj diff --git a/doc/tools/html/icons/up.gif b/doc/tools/html/icons/up.gif deleted file mode 100644 index a9d3e13e47a031bc4ab5e0bf8485fc844233e2a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 316 zcmZ?wbhEHbRA5kGIP#z2|Ns9p&;0K{^tEm0o0|2HikIEYoO3>Y($TQaJwElDoXb{P z{iKTTfktP#y~XC0jUJp#lU(nL7^|jk}GZ1DyGA0K4@74E;;hU z!=beHi6QfP@kW7$Rm>AP92GsgpI-VXvUSO&qs0z~y#x=;y`FtQwrwxVN5NFNRPPfD z9dcN9?Y-S~&zMcXg_DsfL(pD3%aqTL%cVWZl+_@Y$B&WMWs;JYso{($6>rrne#16Z zW|t6E?lPlP)f!WMRi+L8ou*BpT0Ab>_o!9~G9T1jbWD|{^rR}A%Yw5?I|X-Nnm+To e62Hr|OFp+4Hwmu0-cb3FmGd>{tcS-G8LR>07;De~ diff --git a/doc/tools/html/icons/up.png b/doc/tools/html/icons/up.png deleted file mode 100644 index a90e0284436f3823439ae6c00a5fdd692a0663ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 577 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy2?0JKuK)l4pLyng|DmsKJKxl- ze^k8eX6Bsp@so~*b?))0-{f4j(kgd>LGm=U$R62%W?{EVF53bI<20adB0;ac!CIhF z#w2fd7oqAsIUsFUJzX3_DsI(Ix-HhKz~M64NIU=IfBR~Y$X4|dxhzrZ1U2!`f+hZ^ zm&+UyX=thNZFqZ3$(*HZ)#JUq4DD;ALk>J%yo5m@;6#0cY2xz7{28yk-^xCS`nzWO zo266P9IBt@>~+1oES71*pOcQcJRLiv0-~2c`um6}H-$A|S=%gM7riSdo=lopdpYOb zOw|LQ!vq)gGckPg+5M@o!H)g&n<$?iejfqORSc|K{2H#e@a5>fFx*hi?#O5&Ec=1Q zmZ^v}f$4(1dGdiQHI;%r$1^TG)Y&BLaGk%ULbzhlI&Nd22YsqSB1(eu(@M${i&7aF zDsB~D2)Op;jIWRXS6`p=KIg&=E*f7l4%)Egin+m6bAu2My;C}RD@#tS)zLfYbIn`# znzyFziSybQz0aLzW%%MP)bQo*kvO2a#$a@ip=GH1RMs z+iK#o&Bw&V#KXj3tBH{z6GLwqhx7L6v&}&5;vnq-iRoY`8Jn##FuP=S!Hj`{YZl{A T_wvJ{KqU;Gu6{1-oD!M - - Python @RELEASE@ Documentation - @DATE@ - - - - - - -
-

Python Documentation

- -

- Release @RELEASE@ -
- @DATE@ -

-
- - - - - - - - - - - -
- - - -
-   - - -   - -
-

- -

-
- See About the Python Documentation - for information on suggesting changes. -
- - diff --git a/doc/tools/html/stdabout.dat b/doc/tools/html/stdabout.dat deleted file mode 100644 index a9b2718..0000000 --- a/doc/tools/html/stdabout.dat +++ /dev/null @@ -1,48 +0,0 @@ -

This document was generated using the - LaTeX2HTML translator. -

- -

- LaTeX2HTML is Copyright © - 1993, 1994, 1995, 1996, 1997, Nikos - Drakos, Computer Based Learning Unit, University of - Leeds, and Copyright © 1997, 1998, Ross - Moore, Mathematics Department, Macquarie University, - Sydney. -

- -

The application of - LaTeX2HTML to the Python - documentation has been heavily tailored by Fred L. Drake, - Jr. Original navigation icons were contributed by Christopher - Petrilli. -

- -
- -

Comments and Questions

- -

General comments and questions regarding this document should - be sent by email to python-docs@python.org. If you find specific errors in - this document, please report the bug at the Python Bug - Tracker at SourceForge. -

- -

Questions regarding how to use the information in this - document should be sent to the Python news group, comp.lang.python, or the Python mailing list (which is gated to the newsgroup and - carries the same content). -

- -

For any of these channels, please be sure not to send HTML email. - Thanks. -

diff --git a/doc/tools/html/style.css b/doc/tools/html/style.css deleted file mode 100644 index 767cf74..0000000 --- a/doc/tools/html/style.css +++ /dev/null @@ -1,88 +0,0 @@ -/* - * The first part of this is the standard CSS generated by LaTeX2HTML, - * with the "empty" declarations removed. - */ - -/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */ -.math { font-family: "Century Schoolbook", serif; } -.math i { font-family: "Century Schoolbook", serif; - font-weight: bold } -.boldmath { font-family: "Century Schoolbook", serif; - font-weight: bold } - -/* Implement both fixed-size and relative sizes: */ -small.xtiny { font-size : xx-small } -small.tiny { font-size : x-small } -small.scriptsize { font-size : smaller } -small.footnotesize { font-size : small } -big.xlarge { font-size : large } -big.xxlarge { font-size : x-large } -big.huge { font-size : larger } -big.xhuge { font-size : xx-large } - -/* - * Document-specific styles come next; - * these are added for the Python documentation. - * - * Note that the size specifications for the H* elements are because - * Netscape on Solaris otherwise doesn't get it right; they all end up - * the normal text size. - */ - -body { color: #000000; - background-color: #ffffff; } - -a:active { color: #ff0000; } -a:visited { color: #551a8b; } -a:link { color: #0000bb; } - -h1, h2, h3, h4, h5, h6 { font-family: avantgarde, sans-serif; - font-weight: bold } -h1 { font-size: 180% } -h2 { font-size: 150% } -h3, h4 { font-size: 120% } -code, tt { font-family: monospace } -var { font-family: times, serif; - font-style: italic; - font-weight: normal } - -.navigation td { background-color: #99ccff; - font-weight: bold; - font-family: avantgarde, sans-serif; - font-size: 110% } - -.release-info { font-style: italic; } - -.titlegraphic { vertical-align: top; } - -.verbatim { color: #00008b } - -.email { font-family: avantgarde, sans-serif } -.mimetype { font-family: avantgarde, sans-serif } -.newsgroup { font-family: avantgarde, sans-serif } -.url { font-family: avantgarde, sans-serif } -.file { font-family: avantgarde, sans-serif } - -.tableheader { background-color: #99ccff; - font-family: avantgarde, sans-serif; } - -.refcount-info { font-style: italic } -.refcount-info .value { font-weight: bold; - color: #006600 } - -/* - * Some decoration for the "See also:" blocks, in part inspired by some of - * the styling on Lars Marius Garshol's XSA pages. - * (The blue in the navigation bars is #99CCFF.) - */ -.seealso { background-color: #fffaf0; - border: thin solid black; - padding: 4pt } - -.seealso .heading { font-size: 110% } - -/* - * Class 'availability' is used for module availability statements at - * the top of modules. - */ -.availability .platform { font-weight: bold } diff --git a/doc/tools/html2texi.pl b/doc/tools/html2texi.pl deleted file mode 100755 index be050b1..0000000 --- a/doc/tools/html2texi.pl +++ /dev/null @@ -1,1750 +0,0 @@ -#! /usr/bin/env perl -# html2texi.pl -- Convert HTML documentation to Texinfo format -# Michael Ernst -# Time-stamp: <1999-01-12 21:34:27 mernst> - -# This program converts HTML documentation trees into Texinfo format. -# Given the name of a main (or contents) HTML file, it processes that file, -# and other files (transitively) referenced by it, into a Texinfo file -# (whose name is chosen from the file or directory name of the argument). -# For instance: -# html2texi.pl api/index.html -# produces file "api.texi". - -# Texinfo format can be easily converted to Info format (for browsing in -# Emacs or the standalone Info browser), to a printed manual, or to HTML. -# Thus, html2texi.pl permits conversion of HTML files to Info format, and -# secondarily enables producing printed versions of Web page hierarchies. - -# Unlike HTML, Info format is searchable. Since Info is integrated into -# Emacs, one can read documentation without starting a separate Web -# browser. Additionally, Info browsers (including Emacs) contain -# convenient features missing from Web browsers, such as easy index lookup -# and mouse-free browsing. - -# Limitations: -# html2texi.pl is currently tuned to latex2html output (and it corrects -# several latex2html bugs), but should be extensible to arbitrary HTML -# documents. It will be most useful for HTML with a hierarchical structure -# and an index, and it recognizes those features as created by latex2html -# (and possibly by some other tools). The HTML tree to be traversed must -# be on local disk, rather than being accessed via HTTP. -# This script requires the use of "checkargs.pm". To eliminate that -# dependence, replace calls to check_args* by @_ (which is always the last -# argument to those functions). -# Also see the "to do" section, below. -# Comments, suggestions, bug fixes, and enhancements are welcome. - -# Troubleshooting: -# Malformed HTML can cause this program to abort, so -# you should check your HTML files to make sure they are legal. - - -### -### Typical usage for the Python documentation: -### - -# (Actually, most of this is in a Makefile instead.) -# The resulting Info format Python documentation is currently available at -# ftp://ftp.cs.washington.edu/homes/mernst/python-info.tar.gz - -# Fix up HTML problems, eg
should be
. - -# html2texi.pl /homes/fish/mernst/tmp/python-doc/html/api/index.html -# html2texi.pl /homes/fish/mernst/tmp/python-doc/html/ext/index.html -# html2texi.pl /homes/fish/mernst/tmp/python-doc/html/lib/index.html -# html2texi.pl /homes/fish/mernst/tmp/python-doc/html/mac/index.html -# html2texi.pl /homes/fish/mernst/tmp/python-doc/html/ref/index.html -# html2texi.pl /homes/fish/mernst/tmp/python-doc/html/tut/index.html - -# Edit the generated .texi files: -# * change @setfilename to prefix "python-" -# * fix up any sectioning, such as for Abstract -# * make Texinfo menus -# * perhaps remove the @detailmenu ... @end detailmenu -# In Emacs, to do all this: -# (progn (goto-char (point-min)) (replace-regexp "\\(@setfilename \\)\\([-a-z]*\\)$" "\\1python-\\2.info") (replace-string "@node Front Matter\n@chapter Abstract\n" "@node Abstract\n@section Abstract\n") (progn (mark-whole-buffer) (texinfo-master-menu 'update-all-nodes)) (save-buffer)) - -# makeinfo api.texi -# makeinfo ext.texi -# makeinfo lib.texi -# makeinfo mac.texi -# makeinfo ref.texi -# makeinfo tut.texi - - -### -### Structure of the code -### - -# To be written... - - -### -### Design decisions -### - -# Source and destination languages -# -------------------------------- -# -# The goal is Info files; I create Texinfo, so I don't have to worry about -# the finer details of Info file creation. (I'm not even sure of its exact -# format.) -# -# Why not start from LaTeX rather than HTML? -# I could hack latex2html itself to produce Texinfo instead, or fix up -# partparse.py (which already translates LaTeX to Teinfo). -# Pros: -# * has high-level information such as index entries, original formatting -# Cons: -# * those programs are complicated to read and understand -# * those programs try to handle arbitrary LaTeX input, track catcodes, -# and more: I don't want to go to that effort. HTML isn't as powerful -# as LaTeX, so there are fewer subtleties. -# * the result wouldn't work for arbitrary HTML documents; it would be -# nice to eventually extend this program to HTML produced from Docbook, -# Frame, and more. - -# Parsing -# ------- -# -# I don't want to view the text as a linear stream; I'd rather parse the -# whole thing and then do pattern matching over the parsed representation (to -# find idioms such as indices, lists of child nodes, etc.). -# * Perl provides HTML::TreeBuilder, which does just what I want. -# * libwww-perl: http://www.linpro.no/lwp/ -# * TreeBuilder: HTML-Tree-0.51.tar.gz -# * Python Parsers, Formatters, and Writers don't really provide the right -# interface (and the version in Grail doesn't correspond to another -# distributed version, so I'm confused about which to be using). I could -# write something in Python that creates a parse tree, but why bother? - -# Other implementation language issues: -# * Python lacks variable declarations, reasonable scoping, and static -# checking tools. I've written some of the latter for myself that make -# my Perl programming a lot safer than my Python programming will be until -# I have a similar suite for that language. - - -########################################################################### -### To do -### - -# Section names: -# Fix the problem with multiple sections in a single file (eg, Abstract in -# Front Matter section). -# Deal with cross-references, as in /homes/fish/mernst/tmp/python-doc/html/ref/types.html:310 -# Index: -# Perhaps double-check that every tag mentioned in the index is found -# in the text. -# Python: email to python-docs@python.org, to get their feedback. -# Compare to existing lib/ Info manual -# Write the hooks into info-look; replace pyliblookup1-1.tar.gz. -# Postpass to remove extra quotation marks around typography already in -# a different font (to avoid double delimiters as in "`code'"); or -# perhaps consider using only font-based markup so that we don't get -# the extra *bold* and `code' markup in Info. - -## Perhaps don't rely on automatic means for adding up, next, prev; I have -## all that info available to me already, so it's not so much trouble to -## add it. (Right?) But it is *so* easy to use Emacs instead... - - -########################################################################### -### Strictures -### - -# man HTML::TreeBuilder -# man HTML::Parser -# man HTML::Element - -# require HTML::ParserWComment; -require HTML::Parser; -require HTML::TreeBuilder; -require HTML::Element; - -use File::Basename; - -use strict; -# use Carp; - -use checkargs; - - -########################################################################### -### Variables -### - -my @section_stack = (); # elements are chapter/section/subsec nodetitles (I think) -my $current_ref_tdf; # for the file currently being processed; - # used in error messages -my $html_directory; -my %footnotes; - -# First element should not be used. -my @sectionmarker = ("manual", "chapter", "section", "subsection", "subsubsection"); - -my %inline_markup = ("b" => "strong", - "code" => "code", - "i" => "emph", - "kbd" => "kbd", - "samp" => "samp", - "strong" => "strong", - "tt" => "code", - "var" => "var"); - -my @deferred_index_entries = (); - -my @index_titles = (); # list of (filename, type) lists -my %index_info = ("Index" => ["\@blindex", "bl"], - "Concept Index" => ["\@cindex", "cp"], - "Module Index" => ["\@mdindex", "md"]); - - -########################################################################### -### Main/contents page -### - -# Process first-level page on its own, or just a contents page? Well, I do -# want the title, author, etc., and the front matter... For now, just add -# that by hand at the end. - - -# data structure possibilities: -# * tree-like (need some kind of stack when processing (or parent pointers)) -# * list of name and depth; remember old and new depths. - -# Each element is a reference to a list of (nodetitle, depth, filename). -my @contents_list = (); - -# The problem with doing fixups on the fly is that some sections may have -# already been processed (and no longer available) by the time we notice -# others with the same name. It's probably better to fully construct the -# contents list (reading in all files of interest) upfront; that will also -# let me do a better job with cross-references, because again, all files -# will already be read in. -my %contents_hash = (); -my %contents_fixups = (); - -my @current_contents_list = (); - -# Merge @current_contents_list into @contents_list, -# and set @current_contents_list to be empty. -sub merge_contents_lists ( ) -{ check_args(0, @_); - - # Three possibilities: - # * @contents_list is empty: replace it by @current_contents_list. - # * prefixes of the two lists are identical: do nothing - # * @current_contents_list is all at lower level than $contents_list[0]; - # prefix @contents_list by @current_contents_list - - if (scalar(@current_contents_list) == 0) - { die "empty current_contents_list"; } - - # if (scalar(@contents_list) == 0) - # { @contents_list = @current_contents_list; - # @current_contents_list = (); - # return; } - - # if (($ {$contents_list[0]}[1]) < ($ {$current_contents_list[0]}[1])) - # { unshift @contents_list, @current_contents_list; - # @current_contents_list = (); - # return; } - - for (my $i=0; $i= scalar(@contents_list)) - { push @contents_list, $ref_c_tdf; - my $title = $ {$ref_c_tdf}[0]; - if (defined $contents_hash{$title}) - { $contents_fixups{$title} = 1; } - else - { $contents_hash{$title} = 1; } - next; } - my $ref_tdf = $contents_list[$i]; - my ($title, $depth, $file) = @{$ref_tdf}; - my ($c_title, $c_depth, $c_file) = @{$ref_c_tdf}; - - if (($title ne $c_title) - && ($depth < $c_depth) - && ($file ne $c_file)) - { splice @contents_list, $i, 0, $ref_c_tdf; - if (defined $contents_hash{$c_title}) - { $contents_fixups{$c_title} = 1; } - else - { $contents_hash{$c_title} = 1; } - next; } - - if (($title ne $c_title) - || ($depth != $c_depth) - || ($file ne $c_file)) - { die ("while processing $ {$current_ref_tdf}[2] at depth $ {$current_ref_tdf}[1], mismatch at index $i:", - "\n main: <<<$title>>> $depth $file", - "\n curr: <<<$c_title>>> $c_depth $c_file"); } - } - @current_contents_list = (); -} - - - -# Set @current_contents_list to a list of (title, href, sectionlevel); -# then merge that list into @contents_list. -# Maybe this function should also produce a map -# from title (or href) to sectionlevel (eg "chapter"?). -sub process_child_links ( $ ) -{ my ($he) = check_args(1, @_); - - # $he->dump(); - if (scalar(@current_contents_list) != 0) - { die "current_contents_list nonempty: @current_contents_list"; } - $he->traverse(\&increment_current_contents_list, 'ignore text'); - - # Normalize the depths; for instance, convert 1,3,5 into 0,1,2. - my %depths = (); - for my $ref_tdf (@current_contents_list) - { $depths{$ {$ref_tdf}[1]} = 1; } - my @sorted_depths = sort keys %depths; - my $current_depth = scalar(@section_stack)-1; - my $current_depth_2 = $ {$current_ref_tdf}[1]; - if ($current_depth != $current_depth_2) - { die "mismatch in current depths: $current_depth $current_depth_2; ", join(", ", @section_stack); } - for (my $i=0; $i 1) - && ($ {$current_contents_list[1]}[0] eq "Contents")) - { my $ref_first_tdf = shift @current_contents_list; - $current_contents_list[0] = $ref_first_tdf; } - - for (my $i=0; $itag eq "li") - { my @li_content = @{$he->content}; - if ($li_content[0]->tag ne "a") - { die "first element of
  • should be "; } - my ($name, $href, @content) = anchor_info($li_content[0]); - # unused $name - my $title = join("", collect_texts($li_content[0])); - $title = texi_remove_punctuation($title); - # The problem with these is that they are formatted differently in - # @menu and @node! - $title =~ s/``/\"/g; - $title =~ s/''/\"/g; - $title =~ s/ -- / /g; - push @current_contents_list, [ $title, $depth, $href ]; } - return 1; -} - -# Simple version for section titles -sub html_to_texi ( $ ) -{ my ($he) = check_args(1, @_); - if (!ref $he) - { return $he; } - - my $tag = $he->tag; - if (exists $inline_markup{$tag}) - { my $result = "\@$inline_markup{$tag}\{"; - for my $elt (@{$he->content}) - { $result .= html_to_texi($elt); } - $result .= "\}"; - return $result; } - else - { $he->dump(); - die "html_to_texi confused by <$tag>"; } -} - - - -sub print_contents_list () -{ check_args(0, @_); - print STDERR "Contents list:\n"; - for my $ref_tdf (@contents_list) - { my ($title, $depth, $file) = @{$ref_tdf}; - print STDERR "$title $depth $file\n"; } -} - - - -########################################################################### -### Index -### - -my $l2h_broken_link_name = "l2h-"; - - -# map from file to (map from anchor name to (list of index texts)) -# (The list is needed when a single LaTeX command like \envvar -# expands to multiple \index commands.) -my %file_index_entries = (); -my %this_index_entries; # map from anchor name to (list of index texts) - -my %file_index_entries_broken = (); # map from file to (list of index texts) -my @this_index_entries_broken; - -my $index_prefix = ""; -my @index_prefixes = (); - -my $this_indexing_command; - -sub print_index_info () -{ check_args(0, @_); - my ($key, $val); - for my $file (sort keys %file_index_entries) - { my %index_entries = %{$file_index_entries{$file}}; - print STDERR "file: $file\n"; - for my $aname (sort keys %index_entries) - { my @entries = @{$index_entries{$aname}}; - if (scalar(@entries) == 1) - { print STDERR " $aname : $entries[0]\n"; } - else - { print STDERR " $aname : ", join("\n " . (" " x length($aname)), @entries), "\n"; } } } - for my $file (sort keys %file_index_entries_broken) - { my @entries = @{$file_index_entries_broken{$file}}; - print STDERR "file: $file\n"; - for my $entry (@entries) - { print STDERR " $entry\n"; } - } -} - - -sub process_index_file ( $$ ) -{ my ($file, $indexing_command) = check_args(2, @_); - # print "process_index_file $file $indexing_command\n"; - - my $he = file_to_tree($html_directory . $file); - # $he->dump(); - - $this_indexing_command = $indexing_command; - $he->traverse(\&process_if_index_dl_compact, 'ignore text'); - undef $this_indexing_command; - # print "process_index_file done\n"; -} - - -sub process_if_index_dl_compact ( $$$ ) -{ my ($he, $startflag) = (check_args(3, @_))[0,1]; # ignore depth argument - if (!$startflag) - { return; } - - if (($he->tag() eq "dl") && (defined $he->attr('compact'))) - { process_index_dl_compact($he); - return 0; } - else - { return 1; } -} - - -# The elements of a
    list from a LaTeX2HTML index: -# * a single space: text to be ignored -# *
    elements with an optional
    element following each one -# Two types of
    elements: -# * Followed by a
    element: the
    contains a single -# string, and the
    contains a whitespace string to be ignored, a -#
    to be recursively processed (with the
    string as a -# prefix), and a whitespace string to be ignored. -# * Not followed by a
    element: contains a list of anchors -# and texts (ignore the texts, which are only whitespace and commas). -# Optionally contains a
    to be recursively processed (with -# the
    string as a prefix) -sub process_index_dl_compact ( $ ) -{ my ($h) = check_args(1, @_); - my @content = @{$h->content()}; - for (my $i = 0; $i < scalar(@content); $i++) - { my $this_he = $content[$i]; - if ($this_he->tag ne "dt") - { $this_he->dump(); - die "Expected
    tag: " . $this_he->tag; } - if (($i < scalar(@content) - 1) && ($content[$i+1]->tag eq "dd")) - { process_index_dt_and_dd($this_he, $content[$i+1]); - $i++; } - else - { process_index_lone_dt($this_he); } } } - - - -# Argument is a
    element. If it contains more than one anchor, then -# the texts of all subsequent ones are "[Link]". Example: -#
    -# -# "$PATH" -# ", " -# -# "[Link]" -# Optionally contains a
    as well. Example: -#
    -# -# "attribute" -#
    -#
    -# -# "assignment" -# ", " -# -# "[Link]" -#
    -# -# "assignment, class" - -sub process_index_lone_dt ( $ ) -{ my ($dt) = check_args(1, @_); - my @dtcontent = @{$dt->content()}; - my $acontent; - my $acontent_suffix; - for my $a (@dtcontent) - { if ($a eq ", ") - { next; } - if (!ref $a) - { $dt->dump; - die "Unexpected
    string element: $a"; } - - if ($a->tag eq "dl") - { push @index_prefixes, $index_prefix; - if (!defined $acontent_suffix) - { die "acontent_suffix not yet defined"; } - $index_prefix .= $acontent_suffix . ", "; - process_index_dl_compact($a); - $index_prefix = pop(@index_prefixes); - return; } - - if ($a->tag ne "a") - { $dt->dump; - $a->dump; - die "Expected anchor in lone
    "; } - - my ($aname, $ahref, @acontent) = anchor_info($a); - # unused $aname - if (scalar(@acontent) != 1) - { die "Expected just one content of in
    : @acontent"; } - if (ref $acontent[0]) - { $acontent[0]->dump; - die "Expected string content of in
    : $acontent[0]"; } - if (!defined($acontent)) - { $acontent = $index_prefix . $acontent[0]; - $acontent_suffix = $acontent[0]; } - elsif (($acontent[0] ne "[Link]") && ($acontent ne ($index_prefix . $acontent[0]))) - { die "Differing content: <<<$acontent>>>, <<<$acontent[0]>>>"; } - - if (!defined $ahref) - { $dt->dump; - die "no HREF in nachor in
    "; } - my ($ahref_file, $ahref_name) = split(/\#/, $ahref); - if (!defined $ahref_name) - { # Reference to entire file - $ahref_name = ""; } - - if ($ahref_name eq $l2h_broken_link_name) - { if (!exists $file_index_entries_broken{$ahref_file}) - { $file_index_entries_broken{$ahref_file} = []; } - push @{$file_index_entries_broken{$ahref_file}}, "$this_indexing_command $acontent"; - next; } - - if (!exists $file_index_entries{$ahref_file}) - { $file_index_entries{$ahref_file} = {}; } - # Don't do this! It appears to make a copy, which is not desired. - # my %index_entries = %{$file_index_entries{$ahref_file}}; - if (!exists $ {$file_index_entries{$ahref_file}}{$ahref_name}) - { $ {$file_index_entries{$ahref_file}}{$ahref_name} = []; } - # { my $oldcontent = $ {$file_index_entries{$ahref_file}}{$ahref_name}; - # if ($acontent eq $oldcontent) - # { die "Multiple identical index entries?"; } - # die "Trying to add $acontent, but already have index entry pointing at $ahref_file\#$ahref_name: ${$file_index_entries{$ahref_file}}{$ahref_name}"; } - - push @{$ {$file_index_entries{$ahref_file}}{$ahref_name}}, "$this_indexing_command $acontent"; - # print STDERR "keys: ", keys %{$file_index_entries{$ahref_file}}, "\n"; - } -} - -sub process_index_dt_and_dd ( $$ ) -{ my ($dt, $dd) = check_args(2, @_); - my $dtcontent; - { my @dtcontent = @{$dt->content()}; - if ((scalar(@dtcontent) != 1) || (ref $dtcontent[0])) - { $dd->dump; - $dt->dump; - die "Expected single string (actual size = " . scalar(@dtcontent) . ") in content of
    : @dtcontent"; } - $dtcontent = $dtcontent[0]; - $dtcontent =~ s/ +$//; } - my $ddcontent; - { my @ddcontent = @{$dd->content()}; - if (scalar(@ddcontent) != 1) - { die "Expected single
    content, got ", scalar(@ddcontent), " elements:\n", join("\n", @ddcontent), "\n "; } - $ddcontent = $ddcontent[0]; } - if ($ddcontent->tag ne "dl") - { die "Expected
    as content of
    , but saw: $ddcontent"; } - - push @index_prefixes, $index_prefix; - $index_prefix .= $dtcontent . ", "; - process_index_dl_compact($ddcontent); - $index_prefix = pop(@index_prefixes); -} - - -########################################################################### -### Ordinary sections -### - -sub process_section_file ( $$$ ) -{ my ($file, $depth, $nodetitle) = check_args(3, @_); - my $he = file_to_tree(($file =~ /^\//) ? $file : $html_directory . $file); - - # print STDERR "process_section_file: $file $depth $nodetitle\n"; - - # Equivalently: - # while ($depth >= scalar(@section_stack)) { pop(@section_stack); } - @section_stack = @section_stack[0..$depth-1]; - - # Not a great nodename fixup scheme; need a more global view - if ((defined $contents_fixups{$nodetitle}) - && (scalar(@section_stack) > 0)) - { my $up_title = $section_stack[$#section_stack]; - # hack for Python Standard Library - $up_title =~ s/^(Built-in|Standard) Module //g; - my ($up_first_word) = split(/ /, $up_title); - $nodetitle = "$up_first_word $nodetitle"; - } - - push @section_stack, $nodetitle; - # print STDERR "new section_stack: ", join(", ", @section_stack), "\n"; - - $he->traverse(\&process_if_child_links, 'ignore text'); - %footnotes = (); - # $he->dump; - $he->traverse(\&process_if_footnotes, 'ignore text'); - - # $he->dump; - - if (exists $file_index_entries{$file}) - { %this_index_entries = %{$file_index_entries{$file}}; - # print STDERR "this_index_entries:\n ", join("\n ", keys %this_index_entries), "\n"; - } - else - { # print STDERR "Warning: no index entries for file $file\n"; - %this_index_entries = (); } - - if (exists $file_index_entries_broken{$file}) - { @this_index_entries_broken = @{$file_index_entries_broken{$file}}; } - else - { # print STDERR "Warning: no index entries for file $file\n"; - @this_index_entries_broken = (); } - - - if ($he->tag() ne "html") - { die "Expected at top level"; } - my @content = @{$he->content()}; - if ((!ref $content[0]) or ($content[0]->tag ne "head")) - { $he->dump; - die " not first element of "; } - if ((!ref $content[1]) or ($content[1]->tag ne "body")) - { $he->dump; - die " not second element of "; } - - $content[1]->traverse(\&output_body); -} - -# stack of things we're inside that are preventing indexing from occurring now. -# These are "h1", "h2", "h3", "h4", "h5", "h6", "dt" (and possibly others?) -my @index_deferrers = (); - -sub push_or_pop_index_deferrers ( $$ ) -{ my ($tag, $startflag) = check_args(2, @_); - if ($startflag) - { push @index_deferrers, $tag; } - else - { my $old_deferrer = pop @index_deferrers; - if ($tag ne $old_deferrer) - { die "Expected $tag at top of index_deferrers but saw $old_deferrer; remainder = ", join(" ", @index_deferrers); } - do_deferred_index_entries(); } -} - - -sub label_add_index_entries ( $;$ ) -{ my ($label, $he) = check_args_range(1, 2, @_); - # print ((exists $this_index_entries{$label}) ? "*" : " "), " label_add_index_entries $label\n"; - # $he is the anchor element - if (exists $this_index_entries{$label}) - { push @deferred_index_entries, @{$this_index_entries{$label}}; - return; } - - if ($label eq $l2h_broken_link_name) - { # Try to find some text to use in guessing which links should point here - # I should probably only look at the previous element, or if that is - # all punctuation, the one before it; collecting all the previous texts - # is a bit of overkill. - my @anchor_texts = collect_texts($he); - my @previous_texts = collect_texts($he->parent, $he); - # 4 elements is arbitrary; ought to filter out punctuation and small words - # first, then perhaps keep fewer. Perhaps also filter out formatting so - # that we can see a larger chunk of text? (Probably not.) - # Also perhaps should do further chunking into words, in case the - # index term isn't a chunk of its own (eg, was in .... - my @candidate_texts = (@anchor_texts, (reverse(@previous_texts))[0..min(3,$#previous_texts)]); - - my $guessed = 0; - for my $text (@candidate_texts) - { # my $orig_text = $text; - if ($text =~ /^[\"\`\'().?! ]*$/) - { next; } - if (length($text) <= 2) - { next; } - # hack for Python manual; maybe defer until failure first time around? - $text =~ s/^sys\.//g; - for my $iterm (@this_index_entries_broken) - { # I could test for zero: LaTeX2HTML's failures in the Python - # documentation are only for items of the form "... (built-in...)" - if (index($iterm, $text) != -1) - { push @deferred_index_entries, $iterm; - # print STDERR "Guessing index term `$iterm' for text `$orig_text'\n"; - $guessed = 1; - } } } - if (!$guessed) - { # print STDERR "No guess in `", join("'; `", @this_index_entries_broken), "' for texts:\n `", join("'\n `", @candidate_texts), "'\n"; - } - } -} - - -# Need to add calls to this at various places. -# Perhaps add HTML::Element argument and do the check for appropriateness -# here (ie, no action if inside

    , etc.). -sub do_deferred_index_entries () -{ check_args(0, @_); - if ((scalar(@deferred_index_entries) > 0) - && (scalar(@index_deferrers) == 0)) - { print TEXI "\n", join("\n", @deferred_index_entries), "\n"; - @deferred_index_entries = (); } -} - -my $table_columns; # undefined if not in a table -my $table_first_column; # boolean - -sub output_body ( $$$ ) -{ my ($he, $startflag) = (check_args(3, @_))[0,1]; # ignore depth argument - - if (!ref $he) - { my $space_index = index($he, " "); - if ($space_index != -1) - { # Why does - # print TEXI texi_quote(substr($he, 0, $space_index+1)); - # give: Can't locate object method "TEXI" via package "texi_quote" - # (Because the definition texi_quote hasn't been seen yet.) - print TEXI &texi_quote(substr($he, 0, $space_index+1)); - do_deferred_index_entries(); - print TEXI &texi_quote(substr($he, $space_index+1)); } - else - { print TEXI &texi_quote($he); } - return; } - - my $tag = $he->tag(); - - # Ordinary text markup first - if (exists $inline_markup{$tag}) - { if ($startflag) - { print TEXI "\@$inline_markup{$tag}\{"; } - else - { print TEXI "\}"; } } - elsif ($tag eq "a") - { my ($name, $href, @content) = anchor_info($he); - if (!$href) - { # This anchor is only here for indexing/cross referencing purposes. - if ($startflag) - { label_add_index_entries($name, $he); } - } - elsif ($href =~ "^(ftp|http|news):") - { if ($startflag) - { # Should avoid second argument if it's identical to the URL. - print TEXI "\@uref\{$href, "; } - else - { print TEXI "\}"; } - } - elsif ($href =~ /^\#(foot[0-9]+)$/) - { # Footnote - if ($startflag) - { # Could double-check name and content, but I'm not - # currently storing that information. - print TEXI "\@footnote\{"; - $footnotes{$1}->traverse(\&output_body); - print TEXI "\}"; - return 0; } } - else - { if ($startflag) - { # cross-references are not active Info links, but no text is lost - print STDERR "Can't deal with internal HREF anchors yet:\n"; - $he->dump; } - } - } - elsif ($tag eq "br") - { print TEXI "\@\n"; } - elsif ($tag eq "body") - { } - elsif ($tag eq "center") - { if (has_single_content_string($he) - && ($ {$he->content}[0] =~ /^ *$/)) - { return 0; } - if ($startflag) - { print TEXI "\n\@center\n"; } - else - { print TEXI "\n\@end center\n"; } - } - elsif ($tag eq "div") - { my $align = $he->attr('align'); - if (defined($align) && ($align eq "center")) - { if (has_single_content_string($he) - && ($ {$he->content}[0] =~ /^ *$/)) - { return 0; } - if ($startflag) - { print TEXI "\n\@center\n"; } - else - { print TEXI "\n\@end center\n"; } } - } - elsif ($tag eq "dl") - { # Recognize "
     ... 
    " paradigm for "@example" - if (has_single_content_with_tag($he, "dd")) - { my $he_dd = $ {$he->content}[0]; - if (has_single_content_with_tag($he_dd, "pre")) - { my $he_pre = $ {$he_dd->content}[0]; - print_pre($he_pre); - return 0; } } - if ($startflag) - { # Could examine the elements, to be cleverer about formatting. - # (Also to use ftable, vtable...) - print TEXI "\n\@table \@asis\n"; } - else - { print TEXI "\n\@end table\n"; } - } - elsif ($tag eq "dt") - { push_or_pop_index_deferrers($tag, $startflag); - if ($startflag) - { print TEXI "\n\@item "; } - else - { } } - elsif ($tag eq "dd") - { if ($startflag) - { print TEXI "\n"; } - else - { } - if (scalar(@index_deferrers) != 0) - { $he->dump; - die "Unexpected <$tag> while inside: (" . join(" ", @index_deferrers) . "); bad HTML?"; } - do_deferred_index_entries(); - } - elsif ($tag =~ /^(font|big|small)$/) - { # Do nothing for now. - } - elsif ($tag =~ /^h[1-6]$/) - { # We don't need this because we never recursively enter the heading content. - # push_or_pop_index_deferrers($tag, $startflag); - my $secname = ""; - my @seclabels = (); - for my $elt (@{$he->content}) - { if (!ref $elt) - { $secname .= $elt; } - elsif ($elt->tag eq "br") - { } - elsif ($elt->tag eq "a") - { my ($name, $href, @acontent) = anchor_info($elt); - if ($href) - { $he->dump; - $elt->dump; - die "Nonsimple anchor in <$tag>"; } - if (!defined $name) - { die "No NAME for anchor in $tag"; } - push @seclabels, $name; - for my $subelt (@acontent) - { $secname .= html_to_texi($subelt); } } - else - { $secname .= html_to_texi($elt); } } - if ($secname eq "") - { die "No section name in <$tag>"; } - if (scalar(@section_stack) == 1) - { if ($section_stack[-1] ne "Top") - { die "Not top? $section_stack[-1]"; } - print TEXI "\@settitle $secname\n"; - print TEXI "\@c %**end of header\n"; - print TEXI "\n"; - print TEXI "\@node Top\n"; - print TEXI "\n"; } - else - { print TEXI "\n\@node $section_stack[-1]\n"; - print TEXI "\@$sectionmarker[scalar(@section_stack)-1] ", texi_remove_punctuation($secname), "\n"; } - for my $seclabel (@seclabels) - { label_add_index_entries($seclabel); } - # This should only happen once per file. - label_add_index_entries(""); - if (scalar(@index_deferrers) != 0) - { $he->dump; - die "Unexpected <$tag> while inside: (" . join(" ", @index_deferrers) . "); bad HTML?"; } - do_deferred_index_entries(); - return 0; - } - elsif ($tag eq "hr") - { } - elsif ($tag eq "ignore") - { # Hack for ignored elements - return 0; - } - elsif ($tag eq "li") - { if ($startflag) - { print TEXI "\n\n\@item\n"; - do_deferred_index_entries(); } } - elsif ($tag eq "ol") - { if ($startflag) - { print TEXI "\n\@enumerate \@bullet\n"; } - else - { print TEXI "\n\@end enumerate\n"; } } - elsif ($tag eq "p") - { if ($startflag) - { print TEXI "\n\n"; } - if (scalar(@index_deferrers) != 0) - { $he->dump; - die "Unexpected <$tag> while inside: (" . join(" ", @index_deferrers) . "); bad HTML?"; } - do_deferred_index_entries(); } - elsif ($tag eq "pre") - { print_pre($he); - return 0; } - elsif ($tag eq "table") - { # Could also indicate common formatting for first column, or - # determine relative widths for columns (or determine a prototype row) - if ($startflag) - { if (defined $table_columns) - { $he->dump; - die "Can't deal with table nested inside $table_columns-column table"; } - $table_columns = table_columns($he); - if ($table_columns < 2) - { $he->dump; - die "Column with $table_columns columns?"; } - elsif ($table_columns == 2) - { print TEXI "\n\@table \@asis\n"; } - else - { print TEXI "\n\@multitable \@columnfractions"; - for (my $i=0; $i<$table_columns; $i++) - { print TEXI " ", 1.0/$table_columns; } - print TEXI "\n"; } } - else - { if ($table_columns == 2) - { print TEXI "\n\@end table\n"; } - else - { print TEXI "\n\@end multitable\n"; } - undef $table_columns; } } - elsif (($tag eq "td") || ($tag eq "th")) - { if ($startflag) - { if ($table_first_column) - { print TEXI "\n\@item "; - $table_first_column = 0; } - elsif ($table_columns > 2) - { print TEXI "\n\@tab "; } } - else - { print TEXI "\n"; } } - elsif ($tag eq "tr") - { if ($startflag) - { $table_first_column = 1; } } - elsif ($tag eq "ul") - { if ($startflag) - { print TEXI "\n\@itemize \@bullet\n"; } - else - { print TEXI "\n\@end itemize\n"; } } - else - { # I used to have a newline before "output_body" here. - print STDERR "output_body: ignoring <$tag> tag\n"; - $he->dump; - return 0; } - - return 1; -} - -sub print_pre ( $ ) -{ my ($he_pre) = check_args(1, @_); - if (!has_single_content_string($he_pre)) - { die "Multiple or non-string content for
    : ", @{$he_pre->content}; }
    -  my $pre_content = $ {$he_pre->content}[0];
    -  print TEXI "\n\@example";
    -  print TEXI &texi_quote($pre_content);
    -  print TEXI "\@end example\n";
    -}
    -
    -sub table_columns ( $ )
    -{ my ($table) = check_args(1, @_);
    -  my $result = 0;
    -  for my $row (@{$table->content})
    -    { if ($row->tag ne "tr")
    -	{ $table->dump;
    -	  $row->dump;
    -	  die "Expected  as table row."; }
    -      $result = max($result, scalar(@{$row->content})); }
    -  return $result;
    -}
    -
    -
    -###########################################################################
    -### Utilities
    -###
    -
    -sub min ( $$ )
    -{ my ($x, $y) = check_args(2, @_);
    -  return ($x < $y) ? $x : $y;
    -}
    -
    -sub max ( $$ )
    -{ my ($x, $y) = check_args(2, @_);
    -  return ($x > $y) ? $x : $y;
    -}
    -
    -sub file_to_tree ( $ )
    -{ my ($file) = check_args(1, @_);
    -
    -  my $tree = new HTML::TreeBuilder;
    -  $tree->ignore_unknown(1);
    -  # $tree->warn(1);
    -  $tree->parse_file($file);
    -  cleanup_parse_tree($tree);
    -  return $tree
    -}
    -
    -
    -sub has_single_content ( $ )
    -{ my ($he) = check_args(1, @_);
    -  if (!ref $he)
    -    { # return 0;
    -      die "Non-reference argument: $he"; }
    -  my $ref_content = $he->content;
    -  if (!defined $ref_content)
    -    { return 0; }
    -  my @content = @{$ref_content};
    -  if (scalar(@content) != 1)
    -    { return 0; }
    -  return 1;
    -}
    -
    -
    -# Return true if the content of the element contains only one element itself,
    -# and that inner element has the specified tag.
    -sub has_single_content_with_tag ( $$ )
    -{ my ($he, $tag) = check_args(2, @_);
    -  if (!has_single_content($he))
    -    { return 0; }
    -  my $content = $ {$he->content}[0];
    -  if (!ref $content)
    -    { return 0; }
    -  my $content_tag = $content->tag;
    -  if (!defined $content_tag)
    -    { return 0; }
    -  return $content_tag eq $tag;
    -}
    -
    -sub has_single_content_string ( $ )
    -{ my ($he) = check_args(1, @_);
    -  if (!has_single_content($he))
    -    { return 0; }
    -  my $content = $ {$he->content}[0];
    -  if (ref $content)
    -    { return 0; }
    -  return 1;
    -}
    -
    -
    -# Return name, href, content.  First two may be undefined; third is an array.
    -# I don't see how to determine if there are more attributes.
    -sub anchor_info ( $ )
    -{ my ($he) = check_args(1, @_);
    -  if ($he->tag ne "a")
    -    { $he->dump;
    -      die "passed non-anchor to anchor_info"; }
    -  my $name = $he->attr('name');
    -  my $href = $he->attr('href');
    -  my @content = ();
    -  { my $ref_content = $he->content;
    -    if (defined $ref_content)
    -      { @content = @{$ref_content}; } }
    -  return ($name, $href, @content);
    -}
    -
    -
    -sub texi_quote ( $ )
    -{ my ($text) = check_args(1, @_);
    -  $text =~ s/([\@\{\}])/\@$1/g;
    -  $text =~ s/ -- / --- /g;
    -  return $text;
    -}
    -
    -# Eliminate bad punctuation (that confuses Makeinfo or Info) for section titles.
    -sub texi_remove_punctuation ( $ )
    -{ my ($text) = check_args(1, @_);
    -
    -  $text =~ s/^ +//g;
    -  $text =~ s/[ :]+$//g;
    -  $text =~ s/^[1-9][0-9.]* +//g;
    -  $text =~ s/,//g;
    -  # Both embedded colons and " -- " confuse makeinfo.  (Perhaps " -- "
    -  # gets converted into " - ", just as "---" would be converted into " -- ",
    -  # so the names end up differing.)
    -  # $text =~ s/:/ -- /g;
    -  $text =~ s/://g;
    -  return $text;
    -}
    -
    -
    -## Do not use this inside `traverse':  it throws off the traversal.  Use
    -## html_replace_by_ignore or html_replace_by_meta instead.
    -# Returns 1 if success, 0 if failure.
    -sub html_remove ( $;$ )
    -{ my ($he, $parent) = check_args_range(1, 2, @_);
    -  if (!defined $parent)
    -    { $parent = $he->parent; }
    -  my $ref_pcontent = $parent->content;
    -  my @pcontent = @{$ref_pcontent};
    -  for (my $i=0; $iparent(undef);
    -	  return 1; } }
    -  die "Didn't find $he in $parent";
    -}
    -
    -
    -sub html_replace ( $$;$ )
    -{ my ($orig, $new, $parent) = check_args_range(2, 3, @_);
    -  if (!defined $parent)
    -    { $parent = $orig->parent; }
    -  my $ref_pcontent = $parent->content;
    -  my @pcontent = @{$ref_pcontent};
    -  for (my $i=0; $iparent($parent);
    -	  $orig->parent(undef);
    -	  return 1; } }
    -  die "Didn't find $orig in $parent";
    -}
    -
    -sub html_replace_by_meta ( $;$ )
    -{ my ($orig, $parent) = check_args_range(1, 2, @_);
    -  my $meta = new HTML::Element "meta";
    -  if (!defined $parent)
    -    { $parent = $orig->parent; }
    -  return html_replace($orig, $meta, $parent);
    -}
    -
    -sub html_replace_by_ignore ( $;$ )
    -{ my ($orig, $parent) = check_args_range(1, 2, @_);
    -  my $ignore = new HTML::Element "ignore";
    -  if (!defined $parent)
    -    { $parent = $orig->parent; }
    -  return html_replace($orig, $ignore, $parent);
    -}
    -
    -
    -
    -###
    -### Collect text elements
    -###
    -
    -my @collected_texts;
    -my $collect_texts_stoppoint;
    -my $done_collecting;
    -
    -sub collect_texts ( $;$ )
    -{ my ($root, $stop) = check_args_range(1, 2, @_);
    -  # print STDERR "collect_texts: $root $stop\n";
    -  $collect_texts_stoppoint = $stop;
    -  $done_collecting = 0;
    -  @collected_texts = ();
    -  $root->traverse(\&collect_if_text); # process texts
    -  # print STDERR "collect_texts => ", join(";;;", @collected_texts), "\n";
    -  return @collected_texts;
    -}
    -
    -sub collect_if_text ( $$$ )
    -{ my $he = (check_args(3, @_))[0]; #  ignore depth and startflag arguments
    -  if ($done_collecting)
    -    { return 0; }
    -  if (!defined $he)
    -    { return 0; }
    -  if (!ref $he)
    -    { push @collected_texts, $he;
    -      return 0; }
    -  if ((defined $collect_texts_stoppoint) && ($he eq $collect_texts_stoppoint))
    -    { $done_collecting = 1;
    -      return 0; }
    -  return 1;
    -}
    -
    -
    -###########################################################################
    -### Clean up parse tree
    -###
    -
    -sub cleanup_parse_tree ( $ )
    -{ my ($he) = check_args(1, @_);
    -  $he->traverse(\&delete_if_navigation, 'ignore text');
    -  $he->traverse(\&delete_extra_spaces, 'ignore text');
    -  $he->traverse(\&merge_dl, 'ignore text');
    -  $he->traverse(\&reorder_dt_and_dl, 'ignore text');
    -  return $he;
    -}
    -
    -
    -## Simpler version that deletes contents but not the element itself.
    -# sub delete_if_navigation ( $$$ )
    -# { my $he = (check_args(3, @_))[0]; # ignore startflag and depth
    -#   if (($he->tag() eq "div") && ($he->attr('class') eq 'navigation'))
    -#     { $he->delete();
    -#       return 0; }
    -#   else
    -#     { return 1; }
    -# }
    -
    -sub delete_if_navigation ( $$$ )
    -{ my ($he, $startflag) = (check_args(3, @_))[0,1]; #  ignore depth argument
    -  if (!$startflag)
    -    { return; }
    -
    -  if (($he->tag() eq "div") && (defined $he->attr('class')) && ($he->attr('class') eq 'navigation'))
    -    { my $ref_pcontent = $he->parent()->content();
    -      # Don't try to modify @pcontent, which appears to be a COPY.
    -      # my @pcontent = @{$ref_pcontent};
    -      for (my $i = 0; $idelete();
    -      return 0; }
    -  else
    -    { return 1; }
    -}
    -
    -sub delete_extra_spaces ( $$$ )
    -{ my ($he, $startflag) = (check_args(3, @_))[0,1]; #  ignore depth argument
    -  if (!$startflag)
    -    { return; }
    -
    -  my $tag = $he->tag;
    -  if ($tag =~ /^(head|html|table|tr|ul)$/)
    -    { delete_child_spaces($he); }
    -  delete_trailing_spaces($he);
    -  return 1;
    -}
    -
    -
    -sub delete_child_spaces ( $ )
    -{ my ($he) = check_args(1, @_);
    -  my $ref_content = $he->content();
    -  for (my $i = 0; $icontent();
    -  if (! defined $ref_content)
    -    { return; }
    -  # Could also check for previous element = /^h[1-6]$/.
    -  for (my $i = 0; $itag =~ /^(br|dd|dl|dt|hr|p|ul)$/))
    -	    { splice(@{$ref_content}, $i, 1);
    -	      $i--; } } }
    -  if ($he->tag =~ /^(dd|dt|^h[1-6]|li|p)$/)
    -    { my $last_elt = $ {$ref_content}[$#{$ref_content}];
    -      if ((defined $last_elt) && ($last_elt =~ /^ *$/))
    -	{ pop @{$ref_content}; } }
    -}
    -
    -
    -# LaTeX2HTML sometimes creates
    -#   
    text -#
    text -# which should actually be: -#
    -#
    text -#
    text -# Since a
    gets added, this ends up looking like -#

    -#

    -#
    -# text1... -#
    -#
    -# text2... -# dt_or_dd1... -# dt_or_dd2... -# which should become -#

    -#

    -#
    -# text1... -#
    -# text2... -# dt_or_dd1... -# dt_or_dd2... - -sub reorder_dt_and_dl ( $$$ ) -{ my ($he, $startflag) = (check_args(3, @_))[0,1]; # ignore depth argument - if (!$startflag) - { return; } - - if ($he->tag() eq "p") - { my $ref_pcontent = $he->content(); - if (defined $ref_pcontent) - { my @pcontent = @{$ref_pcontent}; - # print "reorder_dt_and_dl found a

    \n"; $he->dump(); - if ((scalar(@pcontent) >= 1) - && (ref $pcontent[0]) && ($pcontent[0]->tag() eq "dl") - && $pcontent[0]->implicit()) - { my $ref_dlcontent = $pcontent[0]->content(); - # print "reorder_dt_and_dl found a

    and implicit

    \n"; - if (defined $ref_dlcontent) - { my @dlcontent = @{$ref_dlcontent}; - if ((scalar(@dlcontent) >= 1) - && (ref $dlcontent[0]) && ($dlcontent[0]->tag() eq "dt")) - { my $ref_dtcontent = $dlcontent[0]->content(); - # print "reorder_dt_and_dl found a

    , implicit

    , and
    \n"; - if (defined $ref_dtcontent) - { my @dtcontent = @{$ref_dtcontent}; - if ((scalar(@dtcontent) > 0) - && (ref $dtcontent[$#dtcontent]) - && ($dtcontent[$#dtcontent]->tag() eq "dl")) - { my $ref_dl2content = $dtcontent[$#dtcontent]->content(); - # print "reorder_dt_and_dl found a

    , implicit

    ,
    , and
    \n"; - if (defined $ref_dl2content) - { my @dl2content = @{$ref_dl2content}; - if ((scalar(@dl2content) > 0) - && (ref ($dl2content[0])) - && ($dl2content[0]->tag() eq "dd")) - { - # print "reorder_dt_and_dl found a

    , implicit

    ,
    ,
    , and
    \n"; - # print STDERR "CHANGING\n"; $he->dump(); - html_replace_by_ignore($dtcontent[$#dtcontent]); - splice(@{$ref_dlcontent}, 1, 0, @dl2content); - # print STDERR "CHANGED TO:\n"; $he->dump(); - return 0; # don't traverse children - } } } } } } } } } - return 1; -} - - -# If we find a paragraph that looks like -#

    -#


    -#