--- /dev/null
+Changelog
+=========
+
+Versions are year-based with a strict backward-compatibility policy.
+The third digit is only for regressions.
+
+19.0.0 (2019-01-21)
+-------------------
+
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- ``X509Store.add_cert`` no longer raises an error if you add a duplicate cert.
+ `#787 <https://github.com/pyca/pyopenssl/pull/787>`_
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+*none*
+
+
+Changes:
+^^^^^^^^
+
+- pyOpenSSL now works with OpenSSL 1.1.1.
+ `#805 <https://github.com/pyca/pyopenssl/pull/805>`_
+- pyOpenSSL now handles NUL bytes in ``X509Name.get_components()``
+ `#804 <https://github.com/pyca/pyopenssl/pull/804>`_
+
+
+
+----
+
+18.0.0 (2018-05-16)
+-------------------
+
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- The minimum ``cryptography`` version is now 2.2.1.
+- Support for Python 2.6 has been dropped.
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+*none*
+
+
+Changes:
+^^^^^^^^
+
+- Added ``Connection.get_certificate`` to retrieve the local certificate.
+ `#733 <https://github.com/pyca/pyopenssl/pull/733>`_
+- ``OpenSSL.SSL.Connection`` now sets ``SSL_MODE_AUTO_RETRY`` by default.
+ `#753 <https://github.com/pyca/pyopenssl/pull/753>`_
+- Added ``Context.set_tlsext_use_srtp`` to enable negotiation of SRTP keying material.
+ `#734 <https://github.com/pyca/pyopenssl/pull/734>`_
+
+
+----
+
+17.5.0 (2017-11-30)
+-------------------
+
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- The minimum ``cryptography`` version is now 2.1.4.
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+*none*
+
+
+Changes:
+^^^^^^^^
+
+- Fixed a potential use-after-free in the verify callback and resolved a memory leak when loading PKCS12 files with ``cacerts``.
+ `#723 <https://github.com/pyca/pyopenssl/pull/723>`_
+- Added ``Connection.export_keying_material`` for RFC 5705 compatible export of keying material.
+ `#725 <https://github.com/pyca/pyopenssl/pull/725>`_
+
+----
+
+
+
+17.4.0 (2017-11-21)
+-------------------
+
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*none*
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+*none*
+
+
+Changes:
+^^^^^^^^
+
+
+- Re-added a subset of the ``OpenSSL.rand`` module.
+ This subset allows conscientious users to reseed the OpenSSL CSPRNG after fork.
+ `#708 <https://github.com/pyca/pyopenssl/pull/708>`_
+- Corrected a use-after-free when reusing an issuer or subject from an ``X509`` object after the underlying object has been mutated.
+ `#709 <https://github.com/pyca/pyopenssl/pull/709>`_
+
+----
+
+
+17.3.0 (2017-09-14)
+-------------------
+
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Dropped support for Python 3.3.
+ `#677 <https://github.com/pyca/pyopenssl/pull/677>`_
+- Removed the deprecated ``OpenSSL.rand`` module.
+ This is being done ahead of our normal deprecation schedule due to its lack of use and the fact that it was becoming a maintenance burden.
+ ``os.urandom()`` should be used instead.
+ `#675 <https://github.com/pyca/pyopenssl/pull/675>`_
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+- Deprecated ``OpenSSL.tsafe``.
+ `#673 <https://github.com/pyca/pyopenssl/pull/673>`_
+
+Changes:
+^^^^^^^^
+
+- Fixed a memory leak in ``OpenSSL.crypto.CRL``.
+ `#690 <https://github.com/pyca/pyopenssl/pull/690>`_
+- Fixed a memory leak when verifying certificates with ``OpenSSL.crypto.X509StoreContext``.
+ `#691 <https://github.com/pyca/pyopenssl/pull/691>`_
+
+
+----
+
+
+17.2.0 (2017-07-20)
+-------------------
+
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*none*
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+- Deprecated ``OpenSSL.rand`` - callers should use ``os.urandom()`` instead.
+ `#658 <https://github.com/pyca/pyopenssl/pull/658>`_
+
+
+Changes:
+^^^^^^^^
+
+- Fixed a bug causing ``Context.set_default_verify_paths()`` to not work with cryptography ``manylinux1`` wheels on Python 3.x.
+ `#665 <https://github.com/pyca/pyopenssl/pull/665>`_
+- Fixed a crash with (EC)DSA signatures in some cases.
+ `#670 <https://github.com/pyca/pyopenssl/pull/670>`_
+
+
+----
+
+
+17.1.0 (2017-06-30)
+-------------------
+
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Removed the deprecated ``OpenSSL.rand.egd()`` function.
+ Applications should prefer ``os.urandom()`` for random number generation.
+ `#630 <https://github.com/pyca/pyopenssl/pull/630>`_
+- Removed the deprecated default ``digest`` argument to ``OpenSSL.crypto.CRL.export()``.
+ Callers must now always pass an explicit ``digest``.
+ `#652 <https://github.com/pyca/pyopenssl/pull/652>`_
+- Fixed a bug with ``ASN1_TIME`` casting in ``X509.set_notBefore()``,
+ ``X509.set_notAfter()``, ``Revoked.set_rev_date()``, ``Revoked.set_nextUpdate()``,
+ and ``Revoked.set_lastUpdate()``. You must now pass times in the form
+ ``YYYYMMDDhhmmssZ``. ``YYYYMMDDhhmmss+hhmm`` and ``YYYYMMDDhhmmss-hhmm``
+ will no longer work. `#612 <https://github.com/pyca/pyopenssl/pull/612>`_
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+
+- Deprecated the legacy "Type" aliases: ``ContextType``, ``ConnectionType``, ``PKeyType``, ``X509NameType``, ``X509ExtensionType``, ``X509ReqType``, ``X509Type``, ``X509StoreType``, ``CRLType``, ``PKCS7Type``, ``PKCS12Type``, ``NetscapeSPKIType``.
+ The names without the "Type"-suffix should be used instead.
+
+
+Changes:
+^^^^^^^^
+
+- Added ``OpenSSL.crypto.X509.from_cryptography()`` and ``OpenSSL.crypto.X509.to_cryptography()`` for converting X.509 certificate to and from pyca/cryptography objects.
+ `#640 <https://github.com/pyca/pyopenssl/pull/640>`_
+- Added ``OpenSSL.crypto.X509Req.from_cryptography()``, ``OpenSSL.crypto.X509Req.to_cryptography()``, ``OpenSSL.crypto.CRL.from_cryptography()``, and ``OpenSSL.crypto.CRL.to_cryptography()`` for converting X.509 CSRs and CRLs to and from pyca/cryptography objects.
+ `#645 <https://github.com/pyca/pyopenssl/pull/645>`_
+- Added ``OpenSSL.debug`` that allows to get an overview of used library versions (including linked OpenSSL) and other useful runtime information using ``python -m OpenSSL.debug``.
+ `#620 <https://github.com/pyca/pyopenssl/pull/620>`_
+- Added a fallback path to ``Context.set_default_verify_paths()`` to accommodate the upcoming release of ``cryptography`` ``manylinux1`` wheels.
+ `#633 <https://github.com/pyca/pyopenssl/pull/633>`_
+
+
+----
+
+
+17.0.0 (2017-04-20)
+-------------------
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*none*
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+*none*
+
+
+Changes:
+^^^^^^^^
+
+- Added ``OpenSSL.X509Store.set_time()`` to set a custom verification time when verifying certificate chains.
+ `#567 <https://github.com/pyca/pyopenssl/pull/567>`_
+- Added a collection of functions for working with OCSP stapling.
+ None of these functions make it possible to validate OCSP assertions, only to staple them into the handshake and to retrieve the stapled assertion if provided.
+ Users will need to write their own code to handle OCSP assertions.
+ We specifically added: ``Context.set_ocsp_server_callback()``, ``Context.set_ocsp_client_callback()``, and ``Connection.request_ocsp()``.
+ `#580 <https://github.com/pyca/pyopenssl/pull/580>`_
+- Changed the ``SSL`` module's memory allocation policy to avoid zeroing memory it allocates when unnecessary.
+ This reduces CPU usage and memory allocation time by an amount proportional to the size of the allocation.
+ For applications that process a lot of TLS data or that use very lage allocations this can provide considerable performance improvements.
+ `#578 <https://github.com/pyca/pyopenssl/pull/578>`_
+- Automatically set ``SSL_CTX_set_ecdh_auto()`` on ``OpenSSL.SSL.Context``.
+ `#575 <https://github.com/pyca/pyopenssl/pull/575>`_
+- Fix empty exceptions from ``OpenSSL.crypto.load_privatekey()``.
+ `#581 <https://github.com/pyca/pyopenssl/pull/581>`_
+
+
+----
+
+
+16.2.0 (2016-10-15)
+-------------------
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*none*
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+*none*
+
+
+Changes:
+^^^^^^^^
+
+- Fixed compatibility errors with OpenSSL 1.1.0.
+- Fixed an issue that caused failures with subinterpreters and embedded Pythons.
+ `#552 <https://github.com/pyca/pyopenssl/pull/552>`_
+
+
+----
+
+
+16.1.0 (2016-08-26)
+-------------------
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+*none*
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+- Dropped support for OpenSSL 0.9.8.
+
+
+Changes:
+^^^^^^^^
+
+- Fix memory leak in ``OpenSSL.crypto.dump_privatekey()`` with ``FILETYPE_TEXT``.
+ `#496 <https://github.com/pyca/pyopenssl/pull/496>`_
+- Enable use of CRL (and more) in verify context.
+ `#483 <https://github.com/pyca/pyopenssl/pull/483>`_
+- ``OpenSSL.crypto.PKey`` can now be constructed from ``cryptography`` objects and also exported as such.
+ `#439 <https://github.com/pyca/pyopenssl/pull/439>`_
+- Support newer versions of ``cryptography`` which use opaque structs for OpenSSL 1.1.0 compatibility.
+
+
+----
+
+
+16.0.0 (2016-03-19)
+-------------------
+
+This is the first release under full stewardship of PyCA.
+We have made *many* changes to make local development more pleasing.
+The test suite now passes both on Linux and OS X with OpenSSL 0.9.8, 1.0.1, and 1.0.2.
+It has been moved to `pytest <https://docs.pytest.org/>`_, all CI test runs are part of `tox <https://tox.readthedocs.io/>`_ and the source code has been made fully `flake8 <https://flake8.readthedocs.io/>`_ compliant.
+
+We hope to have lowered the barrier for contributions significantly but are open to hear about any remaining frustrations.
+
+
+Backward-incompatible changes:
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- Python 3.2 support has been dropped.
+ It never had significant real world usage and has been dropped by our main dependency ``cryptography``.
+ Affected users should upgrade to Python 3.3 or later.
+
+
+Deprecations:
+^^^^^^^^^^^^^
+
+- The support for EGD has been removed.
+ The only affected function ``OpenSSL.rand.egd()`` now uses ``os.urandom()`` to seed the internal PRNG instead.
+ Please see `pyca/cryptography#1636 <https://github.com/pyca/cryptography/pull/1636>`_ for more background information on this decision.
+ In accordance with our backward compatibility policy ``OpenSSL.rand.egd()`` will be *removed* no sooner than a year from the release of 16.0.0.
+
+ Please note that you should `use urandom <https://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/>`_ for all your secure random number needs.
+- Python 2.6 support has been deprecated.
+ Our main dependency ``cryptography`` deprecated 2.6 in version 0.9 (2015-05-14) with no time table for actually dropping it.
+ pyOpenSSL will drop Python 2.6 support once ``cryptography`` does.
+
+
+Changes:
+^^^^^^^^
+
+- Fixed ``OpenSSL.SSL.Context.set_session_id``, ``OpenSSL.SSL.Connection.renegotiate``, ``OpenSSL.SSL.Connection.renegotiate_pending``, and ``OpenSSL.SSL.Context.load_client_ca``.
+ They were lacking an implementation since 0.14.
+ `#422 <https://github.com/pyca/pyopenssl/pull/422>`_
+- Fixed segmentation fault when using keys larger than 4096-bit to sign data.
+ `#428 <https://github.com/pyca/pyopenssl/pull/428>`_
+- Fixed ``AttributeError`` when ``OpenSSL.SSL.Connection.get_app_data()`` was called before setting any app data.
+ `#304 <https://github.com/pyca/pyopenssl/pull/304>`_
+- Added ``OpenSSL.crypto.dump_publickey()`` to dump ``OpenSSL.crypto.PKey`` objects that represent public keys, and ``OpenSSL.crypto.load_publickey()`` to load such objects from serialized representations.
+ `#382 <https://github.com/pyca/pyopenssl/pull/382>`_
+- Added ``OpenSSL.crypto.dump_crl()`` to dump a certificate revocation list out to a string buffer.
+ `#368 <https://github.com/pyca/pyopenssl/pull/368>`_
+- Added ``OpenSSL.SSL.Connection.get_state_string()`` using the OpenSSL binding ``state_string_long``.
+ `#358 <https://github.com/pyca/pyopenssl/pull/358>`_
+- Added support for the ``socket.MSG_PEEK`` flag to ``OpenSSL.SSL.Connection.recv()`` and ``OpenSSL.SSL.Connection.recv_into()``.
+ `#294 <https://github.com/pyca/pyopenssl/pull/294>`_
+- Added ``OpenSSL.SSL.Connection.get_protocol_version()`` and ``OpenSSL.SSL.Connection.get_protocol_version_name()``.
+ `#244 <https://github.com/pyca/pyopenssl/pull/244>`_
+- Switched to ``utf8string`` mask by default.
+ OpenSSL formerly defaulted to a ``T61String`` if there were UTF-8 characters present.
+ This was changed to default to ``UTF8String`` in the config around 2005, but the actual code didn't change it until late last year.
+ This will default us to the setting that actually works.
+ To revert this you can call ``OpenSSL.crypto._lib.ASN1_STRING_set_default_mask_asc(b"default")``.
+ `#234 <https://github.com/pyca/pyopenssl/pull/234>`_
+
+
+----
+
+
+Older Changelog Entries
+-----------------------
+
+The changes from before release 16.0.0 are preserved in the `repository <https://github.com/pyca/pyopenssl/blob/master/doc/ChangeLog_old.txt>`_.
--- /dev/null
+Contributor Covenant Code of Conduct
+====================================
+
+Our Pledge
+----------
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+Our Standards
+-------------
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+Our Responsibilities
+--------------------
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+Scope
+-----
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
+Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
+Representation of a project may be further defined and clarified by project maintainers.
+
+Enforcement
+-----------
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting either the project maintainer Hynek Schlawack at hs@ox.cx or -- e.g. in case of a conflict of interest -- Amber Brown at hawkowl@atleastfornow.net.
+All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances.
+The project team is obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+Attribution
+-----------
+
+This Code of Conduct is adapted from the `Contributor Covenant <http://contributor-covenant.org>`_, version 1.4, available at http://contributor-covenant.org/version/1/4.
--- /dev/null
+Contributing
+============
+
+First of all, thank you for your interest in contributing to pyOpenSSL!
+This project has no company backing its development therefore we're dependent on help by the community.
+
+
+Filing bug reports
+------------------
+
+Bug reports are very welcome.
+Please file them on the `GitHub issue tracker`_.
+Good bug reports come with extensive descriptions of the error and how to reproduce it.
+Reporters are strongly encouraged to include an `short, self contained, correct example <http://www.sscce.org/>`_.
+
+
+Patches
+-------
+
+All patches to pyOpenSSL should be submitted in the form of pull requests to the main pyOpenSSL repository, `pyca/pyopenssl`_.
+These pull requests should satisfy the following properties:
+
+
+Code
+^^^^
+
+- The pull request should focus on one particular improvement to pyOpenSSL.
+ Create different pull requests for unrelated features or bugfixes.
+- Code should follow `PEP 8`_, especially in the "do what code around you does" sense.
+ Follow OpenSSL naming for callables whenever possible is preferred.
+- Pull requests that introduce code must test all new behavior they introduce as well as for previously untested or poorly tested behavior that they touch.
+- Pull requests are not allowed to break existing tests.
+ We usually don't comment on pull requests that are breaking the CI because we consider them work in progress.
+ Please note that not having 100% code coverage for the code you wrote/touched also causes our CI to fail.
+
+
+Documentation
+^^^^^^^^^^^^^
+
+When introducing new functionality, please remember to write documentation.
+
+- New functions and methods should have a docstring describing what they do, what parameters they takes, what types those parameters are, and what they return.
+
+ .. code-block:: python
+
+ def dump_publickey(type, pkey):
+ """
+ Dump a public key to a buffer.
+
+ :param type: The file type (one of :data:`FILETYPE_PEM` or
+ :data:`FILETYPE_ASN1`).
+ :param PKey pkey: The PKey to dump.
+
+ :return: The buffer with the dumped key in it.
+ :rtype: bytes
+ """
+
+
+ Don't forget to add an ``.. auto(function|class|method)::`` statement to the relevant API document found in ``doc/api/`` to actually add your function to the Sphinx documentation.
+- Do *not* use ``:py:`` prefixes when cross-linking (Python is default).
+ Do *not* use the generic ``:data:`` or ``:obj:``.
+ Instead use more specific types like ``:class:``, ``:func:`` or ``:meth:`` if applicable.
+- Pull requests that introduce features or fix bugs should note those changes in the CHANGELOG.rst_ file.
+ Please add new entries to the *top* of the *current* Changes section followed by a line linking to the relevant pull request:
+
+ .. code-block:: rst
+
+ - Added ``OpenSSL.crypto.some_func()`` to do something awesome.
+ [`#1 <https://github.com/pyca/pyopenssl/pull/1>`_]
+
+
+- Use `semantic newlines`_ in reStructuredText_ files (files ending in ``.rst``).
+
+
+Review
+------
+
+Finally, pull requests must be reviewed before merging.
+This process mirrors the `cryptography code review process`_.
+Everyone can perform reviews; this is a very valuable way to contribute, and is highly encouraged.
+
+Pull requests are merged by `members of PyCA`_.
+They should, of course, keep all the requirements detailed in this document as well as the ``pyca/cryptography`` merge requirements in mind.
+
+The final responsibility for the reviewing of merged code lies with the person merging it.
+Since pyOpenSSL is a sensitive project from a security perspective, reviewers are strongly encouraged to take this review and merge process very seriously.
+
+
+Finding Help
+------------
+
+If you need any help with the contribution process, you'll find us hanging out at ``#cryptography-dev`` on Freenode_ IRC.
+You can also ask questions on our `mailing list`_.
+
+Please note that this project is released with a Contributor `Code of Conduct`_.
+By participating in this project you agree to abide by its terms.
+
+
+Security
+--------
+
+If you feel that you found a security-relevant bug that you would prefer to discuss in private, please send us a GPG_-encrypted e-mail.
+
+The maintainer can be reached at hs@ox.cx and his GPG key ID is ``0xAE2536227F69F181`` (Fingerprint: ``C2A0 4F86 ACE2 8ADC F817 DBB7 AE25 3622 7F69 F181``).
+Feel free to cross-check this information with Keybase_.
+
+
+.. _GitHub issue tracker: https://github.com/pyca/pyopenssl/issues
+.. _GPG: https://en.wikipedia.org/wiki/GNU_Privacy_Guard
+.. _Keybase: https://keybase.io/hynek
+.. _pyca/pyopenssl: https://github.com/pyca/pyopenssl
+.. _PEP 8: https://www.python.org/dev/peps/pep-0008/
+.. _cryptography code review process: https://cryptography.io/en/latest/development/reviewing-patches/
+.. _freenode: https://freenode.net
+.. _mailing list: https://mail.python.org/mailman/listinfo/cryptography-dev
+.. _members of PyCA: https://github.com/orgs/pyca/people
+.. _semantic newlines: http://rhodesmill.org/brandon/2012/one-sentence-per-line/
+.. _reStructuredText: http://sphinx-doc.org/rest.html
+.. _CHANGELOG.rst: https://github.com/pyca/pyopenssl/blob/master/CHANGELOG.rst
+.. _`Code of Conduct`: https://github.com/pyca/pyopenssl/blob/master/CODE_OF_CONDUCT.rst
+++ /dev/null
-2011-09-02 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * Release 0.13
-
-2011-06-12 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * OpenSSL/crypto/pkey.c: Add the PKey.check method, mostly
- implemented by Rick Dean, to verify the internal consistency of a
- PKey instance.
-
-2011-06-12 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * OpenSSL/crypto/crypto.c: Fix the sign and verify functions so
- they handle data with embedded NULs. Fix by David Brodsky
- <lp:~lihalla>.
-
-2011-05-20 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * OpenSSL/ssl/connection.c, OpenSSL/test/test_ssl.py: Add a new
- method to the Connection type, get_peer_cert_chain, for retrieving
- the peer's certificate chain.
-
-2011-05-19 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * OpenSSL/crypto/x509.c, OpenSSL/test/test_crypto.py: Add a new
- method to the X509 type, get_signature_algorithm, for inspecting
- the signature algorithm field of the certificate. Based on a
- patch from <lp:~okuda>.
-
-2011-05-10 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * OpenSSL/crypto/crypto.h: Work around a Windows/OpenSSL 1.0 issue
- explicitly including a Windows header before any OpenSSL headers.
-
- * OpenSSL/crypto/pkcs12.c: Work around an OpenSSL 1.0 issue by
- explicitly flushing errors known to be uninteresting after calling
- PKCS12_parse.
-
- * OpenSSL/ssl/context.c: Remove SSLv2 support if the underlying
- OpenSSL library does not provide it.
-
- * OpenSSL/test/test_crypto.py: Support an OpenSSL 1.0 change from
- MD5 to SHA1 by allowing either hash algorithm's result as the
- return value of X509.subject_name_hash.
-
- * OpenSSL/test/test_ssl.py: Support an OpenSSL 1.0 change from MD5
- to SHA1 by constructing certificate files named using both hash
- algorithms' results when testing Context.load_verify_locations.
-
- * Support OpenSSL 1.0.0a.
-
-2011-04-15 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * OpenSSL/ssl/ssl.c: Add OPENSSL_VERSION_NUMBER, SSLeay_version
- and related constants for retrieving version information about the
- underlying OpenSSL library.
-
-2011-04-07 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * Release 0.12
-
-2011-04-06 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * OpenSSL/crypto/x509.c: Add get_extension_count and get_extension
- to the X509 type, allowing read access to certificate extensions.
-
- * OpenSSL/crypto/x509ext.c: Add get_short_name and get_data to the
- X509Extension type, allowing read access to the contents of an
- extension.
-
-2011-03-21 Olivier Hervieu <lp:~ohe>
-
- * OpenSSL/ssl/ssl.c: Expose a number of symbolic constants for
- values passed to the connection "info" callback.
-
-2011-01-22 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * OpenSSL/ssl/connection.py: Add support for new-style
- buffers (primarily memoryviews) to Connection.send and
- Connection.sendall.
-
-2010-11-01 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * Release 0.11
-
-2010-10-07 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * Initial support for Python 3.x throughout the codebase.
-
-2010-09-14 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * OpenSSL/crypto/netscape_spki.c: Fix an off-by-one mistake in the
- error handling for NetscapeSPKI.verify. Add additional error
- checking to NetscapeSPKI.sign to handle the case where there is no
- private key.
-
- * OpenSSL/crypto/x509.c: Fix an overflow bug in the subject_name_hash
- method of the X509 type which would cause it to return negative
- values on 32 bit systems.
-
- * OpenSSL/crypto/x509req.c: Fix an off-by-one mistake in the error
- handling for X509Req.verify.
-
- * OpenSSL/ssl/context.c: Fix the error handling in the load_tmp_dh
- method of the Context type which would cause it to always raise
- MemoryError, regardless of the actual error (such as a bad file
- name).
-
- * OpenSSL/test/: Numerous unit tests added, both for above fixes
- and for other previously untested code paths.
-
-2010-07-27 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * Re-arrange the repository so that the package can be built and
- used in-place without requiring installation.
-
-2010-02-27 James Yonan <james@openvpn.net>
-
- * src/crypto/crypto.c: Added crypto.sign and crypto.verify methods
- that wrap EVP_Sign and EVP_Verify function families, using code
- derived from Dave Cridland's PyOpenSSL branch.
-
- * test/test_crypto.py: Added unit tests for crypto.sign and
- crypto.verify.
-
-2010-01-27 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/ssl/connection.c, src/util.h: Apply patch from Sandro Tosi to
- fix misspellings of "compatibility".
-
-2009-11-13 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * Release 0.10
-
-2009-11-07 Žiga Seilnacht, Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/ssl/connection.c, src/ssl/context.c: Add set_client_ca_list,
- add_client_ca, and get_client_ca_list to Context for manipulating
- the list of certificate authority names which are sent by servers
- with the certificate request message.
- * src/util.h: Add ssize-related defines if the version of Python
- being used does not have them.
- * setup.py: Significant changes to the way Windows builds are done,
- particularly the way OpenSSL headers and libraries are found (with
- the new --with-openssl argument to build_ext).
-
-2009-08-27 Rick Dean <rick@fdd.com>, Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/crypto/pkcs12.c: Add setters to the PKCS12 type for the
- certificate, private key, ca certificate list, and friendly
- name, and add a getter for the friendly name. Also add a method
- for exporting a PKCS12 object as a string.
- * test/test_crypto.py: Add lots of additional tests for the PKCS12
- type.
- * doc/pyOpenSSL.tex: Documentation for the new PKCS12 methods.
-
-2009-07-17 Rick Dean <rick@fdd.com>, Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/crypto/x509ext.c: Add subject and issuer parameters to
- X509Extension, allowing creation of extensions which require that
- information. Fixes LP#322813.
-
-2009-07-16 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * test/util.py: Changed the base TestCase's tearDown to assert that
- no errors were left in the OpenSSL error queue by the test.
- * src/crypto/crypto.c: Add a private helper in support of the
- TestCase.tearDown change.
- * src/crypto/x509name.c: Changed X509Name's getattr implementation
- to clean up the error queue. Fixes LP#314814.
- * test/util.c: Changed flush_error_queue to avoid a reference
- counting bug caused by macro expansion.
-
-2009-07-16 Rick Dean <rick@fdd.com>
-
- * src/rand.c: Added OpenSSL.rand.bytes to get random bytes directly.
- * src/util.c: Added generic exceptions_from_error_queue to replace
- the various other implementations of this function. Also updated
- the rest of the codebase to use this version instead.
-
-2009-07-05 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * test/util.py, test/test_ssl.py, test/test_crypto.py: Fold the
- Python 2.3 compatibility TestCase mixin into the TestCase defined
- in util.py.
-
-2009-07-05 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * test/util.py, test/test_ssl.py, test/test_crypto.py: Stop trying
- to use Twisted's TestCase even when it's available. Instead,
- always use the stdlib TestCase with a few enhancements.
-
-2009-07-04 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * Changed most extension types so that they can be instantiated
- using the type object rather than a factory function. The old
- factory functions are now aliases for the type objects.
- Fixes LP#312786.
-
-2009-05-27 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * Changed all docstrings in extension modules to be friendlier
- towards Python programmers. Fixes LP#312787.
-
-2009-05-27 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/crypto/x509ext.c: Correctly deallocate the new Extension
- instance when there is an error initializing it and it is not
- going to be returned. Resolves LP#368043.
-
-2009-05-11 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * test/test_crypto.py: Use binary mode for the pipe to talk to the
- external openssl binary. The data being transported over this
- pipe is indeed binary, so previously it would often be truncated
- or otherwise mangled.
-
- * src/ssl/connection.h, src/ssl/connection.c, test/test_ssl.py:
- Extend the Connection class with support for in-memory BIOs. This
- allows SSL to be run without a real socket, useful for
- implementing EAP-TLS or using SSL with Windows IO completion
- ports, for example. Based heavily on contributions from Rick
- Dean.
-
-2009-04-25 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * Release 0.9
-
-2009-04-01 Jean-Paul Calderone <exarkun@twistedmatrix.com>
- Samuele Pedroni <pedronis@openend.se>
-
- * src/util.h: Delete the TLS key before trying to set a new value
- for it in case the current thread identifier is a recycled one (if
- it is recycled, the key won't be set because there is already a
- value from the previous thread to have this identifier and to use
- the pyOpenSSL API).
-
-2009-04-01 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/crypto/crypto.c: Add FILETYPE_TEXT for dumping keys and
- certificates and certificate signature requests to a text format.
-
-2008-12-31 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/crypto/x509ext.c, test/test_crypto.py: Add the get_short_name
- method to X509Extension based on patch from Alex Stapleton.
-
-2008-12-31 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/crypto/x509ext.c, test/test_crypto.py: Fix X509Extension so
- that it is possible to instantiate extensions which use s2i or r2i
- instead of v2i (an extremely obscure extension implementation
- detail).
-
-2008-12-30 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * MANIFEST.in, src/crypto/crypto.c, src/crypto/x509.c,
- src/crypto/x509name.c, src/rand/rand.c, src/ssl/context.c: Changes
- which eliminate compiler warnings but should not change any
- behavior.
-
-2008-12-28 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * test/test_ssl.py, src/ssl/ssl.c: Expose DTLS-related constants,
- OP_NO_QUERY_MTU, OP_COOKIE_EXCHANGE, and OP_NO_TICKET.
-
-2008-12-28 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/ssl/context.c: Add a capath parameter to
- Context.load_verify_locations to allow Python code to specify
- either or both arguments to the underlying
- SSL_CTX_load_verify_locations API.
- * src/ssl/context.c: Add Context.set_default_verify_paths, a wrapper
- around SSL_CTX_set_default_verify_paths.
-
-2008-12-28 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * test/test_crypto.py, src/crypto/x509req.c: Added get_version and
- set_version_methods to X509ReqType based on patch from Wouter van
- Bommel. Resolves LP#274418.
-
-2008-09-22 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * Release 0.8
-
-2008-10-19 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * tsafe.py: Revert the deprecation of the thread-safe Connection
- wrapper. The Connection class should not segfault if used from
- multiple threads now, but it generally cannot be relied on to
- produce correct results if used without the thread-safe wrapper.
- * doc/pyOpenSSL.tex: Correct the documentation for the set_passwd_cb
- callback parameter so that it accurately describes the required
- signature.
-
-2008-09-22 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * Release 0.8a1
-
-2008-09-21 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/ssl/ssl.h, src/ssl/ssl.c: Add a thread-local storage key
- which will be used to store and retrieve PyThreadState pointers
- whenever it is necessary to release or re-acquire the GIL.
-
- * src/ssl/context.c: Change global_verify_callback so that it
- unconditionally manipulates the Python threadstate, rather than
- checking the tstate field which is now always NULL.
-
-2008-04-26 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/ssl/context.c: Change global_passphrase_callback and
- global_info_callback so that they acquire the GIL before
- invoking any CPython APIs and do not release it until after they
- are finished invoking all of them (based heavily on on patch
- from Dan Williams).
- * src/ssl/crypto.c: Initialize OpenSSL thread support so that it
- is valid to use OpenSSL APIs from more than one thread (based on
- patch from Dan Williams).
- * test/test_crypto.py: Add tests for load_privatekey and
- dump_privatekey when a passphrase or a passphrase callback is
- supplied.
- * test/test_ssl.py: Add tests for Context.set_passwd_cb and
- Context.set_info_callback.
-
-2008-04-11 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * Release 0.7
-
-2008-03-26 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/crypto/x509name.c: Add X509Name.get_components
-
-2008-03-25 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/crypto/x509name.c: Add hash and der methods to X509Name.
- * src/crypto/x509.c: Fix a bug in X509.get_notBefore and
- X509.get_notAfter preventing UTCTIME format timestamps from
- working.
-
-2008-03-12 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * Fix coding problems in examples/. Remove keys and certificates
- and add a note about how to generate new ones.
-
-2008-03-09 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/crypto/x509.c: Add getters and setters for the notBefore and
- notAfter attributes of X509s.
- * src/crypto/pkey.h, src/crypto/pkey.c, src/crypto/x509req.c,
- src/crypto/x509.c: Track the initialized and public/private state
- of EVP_PKEY structures underlying the crypto_PKeyObj type and
- reject X509Req signature operations on keys not suitable for the
- task.
-
-2008-03-06 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/crypto/x509name.c: Fix tp_compare so it only returns -1, 0, or
- 1. This eliminates a RuntimeWarning emitted by Python.
- * src/crypto/x509req.c: Fix reference counting for X509Name returned
- by X509Req.get_subject. This removes a segfault when the subject
- name outlives the request object.
- * src/crypto/x509.c: Change get_serial_number and set_serial_number
- to accept Python longs.
- * doc/pyOpenSSL.tex: A number of minor corrections.
-
-2008-03-03 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/crypto/crypto.c: Expose X509_verify_cert_error_string. (patch
- from Victor Stinner)
-
-2008-02-22 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/ssl/connection.c src/ssl/context.c src/ssl/ssl.c: Fix
- compilation on Windows. (patch from Michael Schneider)
-
-2008-02-21 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/ssl/connection.c: Expose SSL_get_shutdown and
- SSL_set_shutdown. (patch from James Knight)
- * src/ssl/ssl.c: Expose SSL_SENT_SHUTDOWN and SSL_RECEIVED_SHUTDOWN.
- (patch from James Knight)
-
-2008-02-19 Jean-Paul Calderone <exarkun@twistedmatrix.com>
-
- * src/ssl/context.c: Expose SSL_CTX_add_extra_chain_cert.
- * src/crypto/x509name.c: Fix memory leaks in __getattr__ and
- __setattr_ implementations.
- * src/crypto/x509.c: Fix memory leak in X509.get_pubkey().
- * leakcheck/: An attempt at a systematic approach to leak
- elimination.
-
-2004-08-13 Martin Sjögren <msjogren@gmail.com>
-
- * Released version 0.6.
-
-2004-08-11 Martin Sjögren <msjogren@gmail.com>
-
- * doc/pyOpenSSL.tex: Updates to the docs.
-
-2004-08-10 Martin Sjögren <msjogren@gmail.com>
-
- * src/crypto/x509.c: Add X509.add_extensions based on a patch
- from Han S. Lee.
- * src/ssl/ssl.c: Add more SSL_OP_ constants. Patch from Mihai
- Ibanescu.
-
-2004-08-09 Martin Sjögren <msjogren@gmail.com>
-
- * setup.py src/crypto/: Add support for Netscape SPKI extensions
- based on a patch from Tollef Fog Heen.
- * src/crypto/crypto.c: Add support for python passphrase callbacks
- based on a patch from Robert Olson.
-
-2004-08-03 Martin Sjögren <msjogren@gmail.com>
-
- * src/ssl/context.c: Applied patch from Frederic Peters to add
- Context.use_certificate_chain_file.
- * src/crypto/x509.c: Applid patch from Tollef Fog Heen to add
- X509.subject_name_hash and X509.digest.
-
-2004-08-02 Martin Sjögren <msjogren@gmail.com>
-
- * src/crypto/crypto.c src/ssl/ssl.c: Applied patch from Bastian
- Kleineidam to fix full names of exceptions.
-
-2004-07-19 Martin Sjögren <msjogren@gmail.com>
-
- * doc/pyOpenSSL.tex: Fix the errors regarding X509Name's field names.
-
-2004-07-18 Martin Sjögren <msjogren@gmail.com>
-
- * examples/certgen.py: Fixed wrong attributes in doc string, thanks
- Remy. (SFbug#913315)
- * __init__.py, setup.py, version.py: Add __version__, as suggested by
- Ronald Oussoren in SFbug#888729.
- * examples/proxy.py: Fix typos, thanks Mihai Ibanescu. (SFpatch#895820)
-
-2003-01-09 Martin Sjögren <martin@strakt.com>
-
- * Use cyclic GC protocol in SSL.Connection, SSL.Context, crypto.PKCS12
- and crypto.X509Name.
-
-2002-12-02 Martin Sjögren <martin@strakt.com>
-
- * tsafe.py: Add some missing methods.
-
-2002-10-06 Martin Sjögren <martin@strakt.com>
-
- * __init__.py: Import tsafe too!
-
-2002-10-05 Martin Sjögren <martin@strakt.com>
-
- * src/crypto/x509name.c: Use unicode strings instead of ordinary
- strings in getattr/setattr. Note that plain ascii strings should
- still work.
-
-2002-09-17 Martin Sjögren <martin@strakt.com>
-
- * Released version 0.5.1.
-
-2002-09-09 Martin Sjögren <martin@strakt.com>
-
- * setup.cfg: Fixed build requirements for rpms.
-
-2002-09-07 Martin Sjögren <martin@strakt.com>
-
- * src/ssl/connection.c: Fix sendall() method. It segfaulted because
- it was too generous about giving away the GIL.
- * Added SecureXMLRPCServer example, contributed by Michal Wallace.
-
-2002-09-06 Martin Sjögren <martin@strakt.com>
-
- * setup.cfg: Updated the build requirements.
- * src/ssl/connection.c: Fix includes for AIX.
-
-2002-09-04 Anders Hammarquist <iko@strakt.com>
-
- * Added type checks in all the other places where we expect
- specific types of objects passed.
-
-2002-09-04 Martin Sjögren <martin@strakt.com>
-
- * src/crypto/crypto.c: Added an explicit type check in the dump_*
- functions, so that they won't die when e.g. None is passed in.
-
-2002-08-25 Martin Sjögren <martin@strakt.com>
-
- * doc/pyOpenSSL.tex: Docs for PKCS12.
-
-2002-08-24 Martin Sjögren <martin@strakt.com>
-
- * src/crypto: Added basic PKCS12 support, thanks to Mark Welch
- <mark@collab.net>
-
-2002-08-16 Martin Sjögren <martin@strakt.com>
-
- * D'oh! Fixes for python 1.5 and python 2.1.
-
-2002-08-15 Martin Sjögren <martin@strakt.com>
-
- * Version 0.5. Yay!
-
-2002-07-25 Martin Sjögren <martin@strakt.com>
-
- * src/ssl/context.c: Added set_options method.
- * src/ssl/ssl.c: Added constants for Context.set_options method.
-
-2002-07-23 Martin Sjögren <martin@strakt.com>
-
- * Updated docs
- * src/ssl/connection.c: Changed the get_cipher_list method to actually
- return a list! WARNING: This change makes the API incompatible with
- earlier versions!
-
-2002-07-15 Martin Sjögren <martin@strakt.com>
-
- * src/ssl/connection.[ch]: Removed the fileno method, it uses the
- transport object's fileno instead.
-
-2002-07-09 Martin Sjögren <martin@strakt.com>
-
- * src/crypto/x509.c src/crypto/x509name.c: Fixed segfault bug where
- you used an X509Name after its X509 had been destroyed.
- * src/crypto/crypto.[ch] src/crypto/x509req.c src/crypto/x509ext.[ch]:
- Added X509 Extension support. Thanks to maas-Maarten Zeeman
- <maas@awanim.com>
- * src/crypto/pkey.c: Added bits() and type() methods.
-
-2002-07-08 Martin Sjögren <martin@strakt.com>
-
- * src/ssl/connection.c: Moved the contents of setup_ssl into the
- constructor, thereby fixing some segfault bugs :)
- * src/ssl/connection.c: Added connect_ex and sendall methods.
- * src/crypto/x509name.c: Cleaned up comparisons and NID lookup.
- Thank you Maas-Maarten Zeeman <maas@awanim.com>
- * src/rand/rand.c: Fix RAND_screen import.
- * src/crypto/crypto.c src/crypto/pkcs7.[ch]: Added PKCS7 management,
- courtesy of Maas-Maarten Zeeman <maas@awanim.com>
- * src/crypto/x509req.c: Added verify method.
-
-2002-06-17 Martin Sjögren <martin@strakt.com>
-
- * rpm/, setup.cfg: Added improved RPM-building stuff, thanks to
- Mihai Ibanescu <misa@redhat.com>
-
-2002-06-14 Martin Sjögren <martin@strakt.com>
-
- * examples/proxy.py: Example code for using OpenSSL through a proxy
- contributed by Mihai Ibanescu <misa@redhat.com>
- * Updated installation instruction and added them to the TeX manual.
-
-2002-06-13 Martin Sjögren <martin@strakt.com>
-
- * src/ssl/context.c: Changed global_verify_callback so that it uses
- PyObject_IsTrue instead of requring ints.
- * Added pymemcompat.h to make the memory management uniform and
- backwards-compatible.
- * src/util.h: Added conditional definition of PyModule_AddObject and
- PyModule_AddIntConstant
- * src/ssl/connection.c: Socket methods are no longer explicitly
- wrapped. fileno() is the only method the transport layer object HAS
- to support, but if you want to use connect, accept or sock_shutdown,
- then the transport layer object has to supply connect, accept
- and shutdown respectively.
-
-2002-06-12 Martin Sjögren <martin@strakt.com>
-
- * Changed comments to docstrings that are visible in Python.
- * src/ssl/connection.c: Added set_connect_state and set_accept_state
- methods. Thanks to Mark Welch <mark@collab.net> for this.
-
-2002-06-11 Martin Sjögren <martin@strakt.com>
-
- * src/ssl/connection.c: accept and connect now use SSL_set_accept_state
- and SSL_set_connect_state respectively, instead of SSL_accept and
- SSL_connect.
- * src/ssl/connection.c: Added want_read and want_write methods.
-
-2002-06-05 Martin Sjögren <martin@strakt.com>
-
- * src/ssl/connection.c: Added error messages for windows. The code is
- copied from Python's socketmodule.c. Ick.
- * src/ssl/connection.c: Changed the parameters to the SysCallError. It
- always has a tuple (number, string) now, even though the number
- might not always be useful.
-
-2002-04-05 Martin Sjögren <md9ms@mdstud.chalmers.se>
-
- * Worked more on the Debian packaging, hopefully the packages
- are getting into the main Debian archive soon.
-
-2002-01-10 Martin Sjögren <martin@strakt.com>
-
- * Worked some more on the Debian packaging, it's turning out real
- nice.
- * Changed format on this file, I'm going to try to be a bit more
- verbose about my changes, and this format makes it easier.
-
-2002-01-08 Martin Sjögren <martin@strakt.com>
-
- * Version 0.4.1
- * Added some example code
- * Added the thread safe Connection object in the 'tsafe' submodule
- * New Debian packaging
-
-2001-08-09 Martin Sjögren <martin@strakt.com>
-
- * Version 0.4
- * Added a compare function for X509Name structures.
- * Moved the submodules to separate .so files, with tiny C APIs so they
- can communicate
- * Skeletal OpenSSL/__init__.py
- * Removed the err submodule, use crypto.Error and SSL.Error instead
-
-2001-08-06 Martin Sjögren <martin@strakt.com>
-
- * Version 0.3
- * Added more types for dealing with certificates (X509Store, X509Req,
- PKey)
- * Functionality to load private keys, certificates and certificate
- requests from memory buffers, and store them too
- * X509 and X509Name objects can now be modified as well, very neat when
- creating certificates ;)
- * Added SSL_MODE_AUTO_RETRY to smooth things for blocking sockets
- * Added a sock_shutdown() method to the Connection type
- * I don't understand why, but I can't use Py_InitModule() to create
- submodules in Python 2.0, the interpreter segfaults on the cleanup
- process when I do. I added a conditional compile on the version
- number, falling back to my own routine. It would of course be nice to
- investigate what is happening, but I don't have the time to do so
- * Do INCREF on the type objects before inserting them in the
- dictionary, so they will never reach refcount 0 (they are, after all,
- statically allocated)
-
-2001-07-30 Martin Sjögren <martin@strakt.com>
-
- * Version 0.2
- * Lots of tweaking and comments in the code
- * Now uses distutils instead of the stupid Setup file
- * Hacked doc/tools/mkhowto, html generation should now work
-
-2001-07-16 Martin Sjögren <martin@strakt.com>
-
- * Initial release (0.1, don't expect much from this one :-)
-
+++ /dev/null
-
-INSTALLATION INSTRUCTIONS FOR pyOpenSSL
-------------------------------------------------------------------------------
-
-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.
-
-
--- 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.
-
-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:
-
- > python setup.py build_ext --with-openssl=C:\path\to\openssl build
-
-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:
-
-> pexports python23.dll > libs\python23.def
-> dlltool --dllname python23.dll --def libs\python23.def \
- --output-lib libs\libpython23.a
-
-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!
-
-To build the text, html, postscript or dvi forms of the documentation, this is
-what you do:
-
- cd doc
- # To make the text-only documentation:
- 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 $
--- /dev/null
+Installation
+============
+
+To install pyOpenSSL::
+
+ $ pip install pyopenssl
+
+If you are installing in order to *develop* on pyOpenSSL, move to the root directory of a pyOpenSSL checkout, and run::
+
+ $ pip install -e .
+
+
+.. warning::
+
+ As of 0.14, pyOpenSSL is a pure-Python project.
+ That means that if you encounter *any* kind of compiler errors, pyOpenSSL's bugtracker is the **wrong** place to report them because we *cannot* help you.
+
+ Please take the time to read the errors and report them/ask help from the appropriate project.
+ The most likely culprit being `cryptography <https://cryptography.io/>`_ that contains OpenSSL's library bindings.
+
+
+Supported OpenSSL Versions
+--------------------------
+
+pyOpenSSL supports the same platforms and releases as the upstream cryptography project `does <https://cryptography.io/en/latest/installation/#supported-platforms>`_.
+Currently that means:
+
+- 1.0.1
+- 1.0.2
+- 1.1.0
+
+If you need support for older releases, the following pinned versions will work:
+
+- **OpenSSL 0.9.8**: ``'pyOpenSSL<17.0' 'cryptography<1.4'``
+- **OpenSSL 1.0.0**: ``'pyOpenSSL<17.1' 'cryptography<1.7'``
+
+You can always find out the versions of pyOpenSSL, cryptography, and the linked OpenSSL by running ``python -m OpenSSL.debug``.
+
+
+Documentation
+-------------
+
+The documentation is written in reStructuredText and built using Sphinx::
+
+ $ cd doc
+ $ make html
-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 examples *
-recursive-include rpm *
-global-exclude *.pyc
+include LICENSE MANIFEST.in *.rst tox.ini .coveragerc
+exclude leakcheck codecov.yml
+recursive-include tests *.py
+recursive-include doc *
+recursive-include examples *
+recursive-include rpm *
+recursive-exclude leakcheck *.py *.pem
+recursive-exclude examples/simple *.cert *.pkey
+prune doc/_build
+prune .travis
+prune .mention-bot
+++ /dev/null
- RATIONALE
-
-The reason this module exists at all is that the SSL support in the socket
-module in the Python 2.1 distribution (which is what we used, of course I
-cannot speak for later versions) is severely limited.
-
-<FIXME> Update this list whenever needed! The communications module isn't
-written yet, so we don't know exactly how this'll work! </FIXME>
-This is a list of things we need from an OpenSSL module:
- + Context objects (in OpenSSL called SSL_CTX) that can be manipulated from
- Python modules. They must support a number of operations:
- - Loading certificates from file and memory, both the client
- certificate and the certificates used for the verification chain.
- - Loading private keys from file and memory.
- - Setting the verification mode (basically VERIFY_NONE and
- VERIFY_PEER).
- - Callbacks mechanism for prompting for pass phrases and verifying
- certificates. The callbacks have to work under a multi-threaded
- environment (see the comment in ssl/context.c). Of course the
- callbacks will have to be written in Python!
- + The Connection objects (in OpenSSL called SSL) have to support a few
- things:
- - Renegotiation, this is really important, especially for connections
- that are up and running for a long time, since renegotiation
- generates new encryption keys.
- - Server-side SSL must work! As far as I know this doesn't work in
- the SSL support of the socket module as of Python 2.1.
- - Wrapping the methods of the underlying transport object is nice, so
- you don't have to keep track of more than one object per connection.
- This could of course be done a lot better than the way it works now,
- so more transport layers than sockets are possible!
- + A well-organized error system that mimics OpenSSL's error system is
- desireable. Specifically there has to be a way to find out wether the
- operation was successful, or if it failed, why it failed, so some sort
- of interface to OpenSSL's error queue mechanism is needed.
- + Certificate objects (X509) and certificate name objects (X509_NAME) are
- needed, especially for verification purposes. Certificates will
- probably also be generated by the server which is another reason for
- them to exist. The same thing goes for key objects (EVP_PKEY)
- + Since this is an OpenSSL module, there has to be an interface to the
- OpenSSL PRNG, so it can be seeded in a good way.
-
-When asking about SSL on the comp.lang.python newsgroup (or on
-python-list@python.org) people usually pointed you to the M2Crypto package.
-The M2Crypto.SSL module does implement a lot of OpenSSL's functionality but
-unfortunately its error handling system does not seem to be finished,
-especially for non-blocking I/O. I think that much of the reason for this
-is that M2Crypto is developed using SWIG. This makes it awkward to create
-functions that e.g. can return both an integer and NULL since (as far as I
-know) you basically write C functions and SWIG makes wrapper functions that
-parses the Python argument list and calls your C function, and finally
-transforms your return value to a Python object.
-
-Finally, a good book on the topic of SSL (that I read and learned a lot
-from) is "SSL and TLS - Designing and Building Secure Systems" (ISBN
-0201615983) by Eric Rescorla. A good mailinglist to subscribe to is the
-openssl-users@openssl.org list.
-
-This comment was written July 2001, discussing Python 2.1. Feel free to
-modify it as the SSL support in the socket module changes.
-
+++ /dev/null
-# Copyright (C) AB Strakt
-# See LICENSE for details.
-
-"""
-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.version import __version__
-
-__all__ = [
- 'rand', 'crypto', 'SSL', 'tsafe', '__version__']
+++ /dev/null
-#include <Python.h>
-#define crypto_MODULE
-#include "crypto.h"
-
-
-static X509_REVOKED * X509_REVOKED_dupe(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_dupe(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_dupe( 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;
-}
+++ /dev/null
-#ifndef PyOpenSSL_crypto_CRL_H_
-#define PyOpenSSL_crypto_CRL_H_
-
-#include <Python.h>
-
-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
+++ /dev/null
-/*
- * 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 <Python.h>
-#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 <pythread.h>
-
-/**
- * This array will store all of the mutexes available to OpenSSL.
- */
-static PyThread_type_lock *mutex_buf = NULL;
-
-
-/**
- * Callback function supplied to OpenSSL to acquire or release a lock.
- *
- */
-static void locking_function(int mode, int n, const char * file, int line) {
- if (mode & CRYPTO_LOCK) {
- PyThread_acquire_lock(mutex_buf[n], WAIT_LOCK);
- } else {
- PyThread_release_lock(mutex_buf[n]);
- }
-}
-
-
-/**
- * Initialize OpenSSL for use from multiple threads.
- *
- * Returns: 0 if initialization fails, 1 otherwise.
- */
-static int init_openssl_threads(void) {
- int i;
-
- mutex_buf = (PyThread_type_lock *)malloc(
- CRYPTO_num_locks() * sizeof(PyThread_type_lock));
- if (!mutex_buf) {
- return 0;
- }
- for (i = 0; i < CRYPTO_num_locks(); ++i) {
- mutex_buf[i] = PyThread_allocate_lock();
- }
- CRYPTO_set_id_callback((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);
- ;
-}
+++ /dev/null
-/*
- * 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 <Python.h>
-/* 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_ */
+++ /dev/null
-/*
- * netscape_spki.c
- *
- * Copyright (C) Tollef Fog Heen
- * See LICENSE for details.
- *
- * Netscape SPKI handling, thin wrapper
- */
-#include <Python.h>
-#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;
-}
+++ /dev/null
-/*
- * 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 <Python.h>
-#include <openssl/ssl.h>
-
-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
+++ /dev/null
-/*
- * 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 <Python.h>
-#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;
-}
+++ /dev/null
-/*
- * 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 <Python.h>
-#include <openssl/pkcs12.h>
-#include <openssl/asn1.h>
-
-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
+++ /dev/null
-/*
- * 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 <Python.h>
-#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;
-}
-
+++ /dev/null
-/*
- * 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 <Python.h>
-#include <openssl/pkcs7.h>
-
-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
+++ /dev/null
-/*
- * 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 <Python.h>
-#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;
-}
-
+++ /dev/null
-/*
- * 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
+++ /dev/null
-#include <Python.h>
-#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;
-}
+++ /dev/null
-#ifndef PyOpenSSL_crypto_REVOKED_H_
-#define PyOpenSSL_crypto_REVOKED_H_
-
-#include <Python.h>
-
-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
+++ /dev/null
-/*
- * 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 <Python.h>
-#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;
-}
-
+++ /dev/null
-/*
- * 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 <Python.h>
-#include <openssl/ssl.h>
-
-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
+++ /dev/null
-/*
- * 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 <Python.h>
-#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;
-}
+++ /dev/null
-/*
- * 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 <Python.h>
-#include <openssl/ssl.h>
-#include <openssl/x509v3.h>
-
-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
-
+++ /dev/null
-/*
- * 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 <Python.h>
-#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, "<X509Name object '%s'>", 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;
-}
+++ /dev/null
-/*
- * 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 <Python.h>
-#include <openssl/ssl.h>
-
-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
+++ /dev/null
-/*
- * 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 <Python.h>
-#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;
-}
+++ /dev/null
-/*
- * 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 <Python.h>
-#include <openssl/ssl.h>
-
-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
+++ /dev/null
-/*
- * 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 <Python.h>
-#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;
-}
+++ /dev/null
-/*
- * 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 <Python.h>
-#include <openssl/ssl.h>
-
-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
+++ /dev/null
-#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_ */
-
+++ /dev/null
-/* 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 */
+++ /dev/null
-/*
- * 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 <Python.h>
-
-/*
- * 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 <openssl/rand.h>
-#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);
- ;
-}
-
+++ /dev/null
-/*
- * 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 <Python.h>
-
-#ifndef MS_WINDOWS
-# include <sys/socket.h>
-# include <netinet/in.h>
-# if !(defined(__BEOS__) || defined(__CYGWIN__))
-# include <netinet/tcp.h>
-# endif
-#else
-# include <winsock.h>
-# include <wincrypt.h>
-#endif
-
-#define SSL_MODULE
-#include <openssl/bio.h>
-#include <openssl/err.h>
-#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;
-}
-
+++ /dev/null
-/*
- * 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 <Python.h>
-#include <openssl/ssl.h>
-
-/* shamelessly stolen from socketmodule.c */
-#ifdef MS_WINDOWS
-# include <winsock.h>
-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
-
+++ /dev/null
-/*
- * 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 <Python.h>
-
-#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 <sys/socket.h>
-# include <netinet/in.h>
-# if !(defined(__BEOS__) || defined(__CYGWIN__))
-# include <netinet/tcp.h>
-# endif
-#else
-# include <winsock.h>
-# include <wincrypt.h>
-#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;
-}
-
+++ /dev/null
-/*
- * 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 <Python.h>
-#include <openssl/ssl.h>
-
-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
+++ /dev/null
-/*
- * 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 <Python.h>
-
-#ifndef MS_WINDOWS
-# include <sys/socket.h>
-# include <netinet/in.h>
-# if !(defined(__BEOS__) || defined(__CYGWIN__))
-# include <netinet/tcp.h>
-# endif
-#else
-# include <winsock.h>
-# include <wincrypt.h>
-#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);
- ;
-}
+++ /dev/null
-/*
- * 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 <Python.h>
-#include <pythread.h>
-#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_ */
+++ /dev/null
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-
-"""
-Package containing unit tests for L{OpenSSL}.
-"""
+++ /dev/null
-# Copyright (c) Jean-Paul Calderone
-# See LICENSE file for details.
-
-"""
-Unit tests for L{OpenSSL.crypto}.
-"""
-
-from unittest import main
-
-import os, re
-from subprocess import PIPE, Popen
-from datetime import datetime, timedelta
-
-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 X509Extension, X509ExtensionType
-from OpenSSL.crypto import load_certificate, load_privatekey
-from OpenSSL.crypto import FILETYPE_PEM, FILETYPE_ASN1, FILETYPE_TEXT
-from OpenSSL.crypto import dump_certificate, load_certificate_request
-from OpenSSL.crypto import dump_certificate_request, dump_privatekey
-from OpenSSL.crypto import PKCS7Type, load_pkcs7_data
-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
-
-def normalize_certificate_pem(pem):
- return dump_certificate(FILETYPE_PEM, load_certificate(FILETYPE_PEM, pem))
-
-
-def normalize_privatekey_pem(pem):
- return dump_privatekey(FILETYPE_PEM, load_privatekey(FILETYPE_PEM, pem))
-
-
-root_cert_pem = b("""-----BEGIN CERTIFICATE-----
-MIIC7TCCAlagAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
-BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU
-ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwIhgPMjAwOTAzMjUxMjM2
-NThaGA8yMDE3MDYxMTEyMzY1OFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklM
-MRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9U
-ZXN0aW5nIFJvb3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPmaQumL
-urpE527uSEHdL1pqcDRmWzu+98Y6YHzT/J7KWEamyMCNZ6fRW1JCR782UQ8a07fy
-2xXsKy4WdKaxyG8CcatwmXvpvRQ44dSANMihHELpANTdyVp6DCysED6wkQFurHlF
-1dshEaJw8b/ypDhmbVIo6Ci1xvCJqivbLFnbAgMBAAGjgbswgbgwHQYDVR0OBBYE
-FINVdy1eIfFJDAkk51QJEo3IfgSuMIGIBgNVHSMEgYAwfoAUg1V3LV4h8UkMCSTn
-VAkSjch+BK6hXKRaMFgxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UE
-BxMHQ2hpY2FnbzEQMA4GA1UEChMHVGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBS
-b290IENBggg9DMTgxt659DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GB
-AGGCDazMJGoWNBpc03u6+smc95dEead2KlZXBATOdFT1VesY3+nUOqZhEhTGlDMi
-hkgaZnzoIq/Uamidegk4hirsCT/R+6vsKAAxNTcBjUeZjlykCJWy5ojShGftXIKY
-w/njVbKMXrvc83qmTdGl3TAM0fxQIpqgcglFLveEBgzn
------END CERTIFICATE-----
-""")
-
-root_key_pem = b("""-----BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQD5mkLpi7q6ROdu7khB3S9aanA0Zls7vvfGOmB80/yeylhGpsjA
-jWen0VtSQke/NlEPGtO38tsV7CsuFnSmschvAnGrcJl76b0UOOHUgDTIoRxC6QDU
-3claegwsrBA+sJEBbqx5RdXbIRGicPG/8qQ4Zm1SKOgotcbwiaor2yxZ2wIDAQAB
-AoGBAPCgMpmLxzwDaUmcFbTJUvlLW1hoxNNYSu2jIZm1k/hRAcE60JYwvBkgz3UB
-yMEh0AtLxYe0bFk6EHah11tMUPgscbCq73snJ++8koUw+csk22G65hOs51bVb7Aa
-6JBe67oLzdtvgCUFAA2qfrKzWRZzAdhUirQUZgySZk+Xq1pBAkEA/kZG0A6roTSM
-BVnx7LnPfsycKUsTumorpXiylZJjTi9XtmzxhrYN6wgZlDOOwOLgSQhszGpxVoMD
-u3gByT1b2QJBAPtL3mSKdvwRu/+40zaZLwvSJRxaj0mcE4BJOS6Oqs/hS1xRlrNk
-PpQ7WJ4yM6ZOLnXzm2mKyxm50Mv64109FtMCQQDOqS2KkjHaLowTGVxwC0DijMfr
-I9Lf8sSQk32J5VWCySWf5gGTfEnpmUa41gKTMJIbqZZLucNuDcOtzUaeWZlZAkA8
-ttXigLnCqR486JDPTi9ZscoZkZ+w7y6e/hH8t6d5Vjt48JVyfjPIaJY+km58LcN3
-6AWSeGAdtRFHVzR7oHjVAkB4hutvxiOeiIVQNBhM6RSI9aBPMI21DoX2JRoxvNW2
-cbvAhow217X9V0dVerEOKxnNYspXRrh36h7k4mQA+sDq
------END RSA PRIVATE KEY-----
-""")
-
-server_cert_pem = b("""-----BEGIN CERTIFICATE-----
-MIICKDCCAZGgAwIBAgIJAJn/HpR21r/8MA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
-BAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UEBxMHQ2hpY2FnbzEQMA4GA1UEChMH
-VGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBSb290IENBMCIYDzIwMDkwMzI1MTIz
-NzUzWhgPMjAxNzA2MTExMjM3NTNaMBgxFjAUBgNVBAMTDWxvdmVseSBzZXJ2ZXIw
-gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAL6m+G653V0tpBC/OKl22VxOi2Cv
-lK4TYu9LHSDP9uDVTe7V5D5Tl6qzFoRRx5pfmnkqT5B+W9byp2NU3FC5hLm5zSAr
-b45meUhjEJ/ifkZgbNUjHdBIGP9MAQUHZa5WKdkGIJvGAvs8UzUqlr4TBWQIB24+
-lJ+Ukk/CRgasrYwdAgMBAAGjNjA0MB0GA1UdDgQWBBS4kC7Ij0W1TZXZqXQFAM2e
-gKEG2DATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQUFAAOBgQBh30Li
-dJ+NlxIOx5343WqIBka3UbsOb2kxWrbkVCrvRapCMLCASO4FqiKWM+L0VDBprqIp
-2mgpFQ6FHpoIENGvJhdEKpptQ5i7KaGhnDNTfdy3x1+h852G99f1iyj0RmbuFcM8
-uzujnS8YXWvM7DM1Ilozk4MzPug8jzFp5uhKCQ==
------END CERTIFICATE-----
-""")
-
-server_key_pem = normalize_privatekey_pem(b("""-----BEGIN RSA PRIVATE KEY-----
-MIICWwIBAAKBgQC+pvhuud1dLaQQvzipdtlcTotgr5SuE2LvSx0gz/bg1U3u1eQ+
-U5eqsxaEUceaX5p5Kk+QflvW8qdjVNxQuYS5uc0gK2+OZnlIYxCf4n5GYGzVIx3Q
-SBj/TAEFB2WuVinZBiCbxgL7PFM1Kpa+EwVkCAduPpSflJJPwkYGrK2MHQIDAQAB
-AoGAbwuZ0AR6JveahBaczjfnSpiFHf+mve2UxoQdpyr6ROJ4zg/PLW5K/KXrC48G
-j6f3tXMrfKHcpEoZrQWUfYBRCUsGD5DCazEhD8zlxEHahIsqpwA0WWssJA2VOLEN
-j6DuV2pCFbw67rfTBkTSo32ahfXxEKev5KswZk0JIzH3ooECQQDgzS9AI89h0gs8
-Dt+1m11Rzqo3vZML7ZIyGApUzVan+a7hbc33nbGRkAXjHaUBJO31it/H6dTO+uwX
-msWwNG5ZAkEA2RyFKs5xR5USTFaKLWCgpH/ydV96KPOpBND7TKQx62snDenFNNbn
-FwwOhpahld+vqhYk+pfuWWUpQciE+Bu7ZQJASjfT4sQv4qbbKK/scePicnDdx9th
-4e1EeB9xwb+tXXXUo/6Bor/AcUNwfiQ6Zt9PZOK9sR3lMZSsP7rMi7kzuQJABie6
-1sXXjFH7nNJvRG4S39cIxq8YRYTy68II/dlB2QzGpKxV/POCxbJ/zu0CU79tuYK7
-NaeNCFfH3aeTrX0LyQJAMBWjWmeKM2G2sCExheeQK0ROnaBC8itCECD4Jsve4nqf
-r50+LF74iLXFwqysVCebPKMOpDWp/qQ1BbJQIPs7/A==
------END RSA PRIVATE KEY-----
-"""))
-
-client_cert_pem = b("""-----BEGIN CERTIFICATE-----
-MIICJjCCAY+gAwIBAgIJAKxpFI5lODkjMA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
-BAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UEBxMHQ2hpY2FnbzEQMA4GA1UEChMH
-VGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBSb290IENBMCIYDzIwMDkwMzI1MTIz
-ODA1WhgPMjAxNzA2MTExMjM4MDVaMBYxFDASBgNVBAMTC3VnbHkgY2xpZW50MIGf
-MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAZh/SRtNm5ntMT4qb6YzEpTroMlq2
-rn+GrRHRiZ+xkCw/CGNhbtPir7/QxaUj26BSmQrHw1bGKEbPsWiW7bdXSespl+xK
-iku4G/KvnnmWdeJHqsiXeUZtqurMELcPQAw9xPHEuhqqUJvvEoMTsnCEqGM+7Dtb
-oCRajYyHfluARQIDAQABozYwNDAdBgNVHQ4EFgQUNQB+qkaOaEVecf1J3TTUtAff
-0fAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEAyv/Jh7gM
-Q3OHvmsFEEvRI+hsW8y66zK4K5de239Y44iZrFYkt7Q5nBPMEWDj4F2hLYWL/qtI
-9Zdr0U4UDCU9SmmGYh4o7R4TZ5pGFvBYvjhHbkSFYFQXZxKUi+WUxplP6I0wr2KJ
-PSTJCjJOn3xo2NTKRgV1gaoTf2EhL+RG8TQ=
------END CERTIFICATE-----
-""")
-
-client_key_pem = normalize_privatekey_pem(b("""-----BEGIN RSA PRIVATE KEY-----
-MIICXgIBAAKBgQDAZh/SRtNm5ntMT4qb6YzEpTroMlq2rn+GrRHRiZ+xkCw/CGNh
-btPir7/QxaUj26BSmQrHw1bGKEbPsWiW7bdXSespl+xKiku4G/KvnnmWdeJHqsiX
-eUZtqurMELcPQAw9xPHEuhqqUJvvEoMTsnCEqGM+7DtboCRajYyHfluARQIDAQAB
-AoGATkZ+NceY5Glqyl4mD06SdcKfV65814vg2EL7V9t8+/mi9rYL8KztSXGlQWPX
-zuHgtRoMl78yQ4ZJYOBVo+nsx8KZNRCEBlE19bamSbQLCeQMenWnpeYyQUZ908gF
-h6L9qsFVJepgA9RDgAjyDoS5CaWCdCCPCH2lDkdcqC54SVUCQQDseuduc4wi8h4t
-V8AahUn9fn9gYfhoNuM0gdguTA0nPLVWz4hy1yJiWYQe0H7NLNNTmCKiLQaJpAbb
-TC6vE8C7AkEA0Ee8CMJUc20BnGEmxwgWcVuqFWaKCo8jTH1X38FlATUsyR3krjW2
-dL3yDD9NwHxsYP7nTKp/U8MV7U9IBn4y/wJBAJl7H0/BcLeRmuJk7IqJ7b635iYB
-D/9beFUw3MUXmQXZUfyYz39xf6CDZsu1GEdEC5haykeln3Of4M9d/4Kj+FcCQQCY
-si6xwT7GzMDkk/ko684AV3KPc/h6G0yGtFIrMg7J3uExpR/VdH2KgwMkZXisSMvw
-JJEQjOMCVsEJlRk54WWjAkEAzoZNH6UhDdBK5F38rVt/y4SEHgbSfJHIAmPS32Kq
-f6GGcfNpip0Uk7q7udTKuX7Q/buZi/C4YW7u3VKAquv9NA==
------END RSA PRIVATE KEY-----
-"""))
-
-cleartextCertificatePEM = b("""-----BEGIN CERTIFICATE-----
-MIIC7TCCAlagAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
-BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU
-ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwIhgPMjAwOTAzMjUxMjM2
-NThaGA8yMDE3MDYxMTEyMzY1OFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklM
-MRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9U
-ZXN0aW5nIFJvb3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPmaQumL
-urpE527uSEHdL1pqcDRmWzu+98Y6YHzT/J7KWEamyMCNZ6fRW1JCR782UQ8a07fy
-2xXsKy4WdKaxyG8CcatwmXvpvRQ44dSANMihHELpANTdyVp6DCysED6wkQFurHlF
-1dshEaJw8b/ypDhmbVIo6Ci1xvCJqivbLFnbAgMBAAGjgbswgbgwHQYDVR0OBBYE
-FINVdy1eIfFJDAkk51QJEo3IfgSuMIGIBgNVHSMEgYAwfoAUg1V3LV4h8UkMCSTn
-VAkSjch+BK6hXKRaMFgxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UE
-BxMHQ2hpY2FnbzEQMA4GA1UEChMHVGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBS
-b290IENBggg9DMTgxt659DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GB
-AGGCDazMJGoWNBpc03u6+smc95dEead2KlZXBATOdFT1VesY3+nUOqZhEhTGlDMi
-hkgaZnzoIq/Uamidegk4hirsCT/R+6vsKAAxNTcBjUeZjlykCJWy5ojShGftXIKY
-w/njVbKMXrvc83qmTdGl3TAM0fxQIpqgcglFLveEBgzn
------END CERTIFICATE-----
-""")
-
-cleartextPrivateKeyPEM = normalize_privatekey_pem(b("""\
------BEGIN RSA PRIVATE KEY-----
-MIICXQIBAAKBgQD5mkLpi7q6ROdu7khB3S9aanA0Zls7vvfGOmB80/yeylhGpsjA
-jWen0VtSQke/NlEPGtO38tsV7CsuFnSmschvAnGrcJl76b0UOOHUgDTIoRxC6QDU
-3claegwsrBA+sJEBbqx5RdXbIRGicPG/8qQ4Zm1SKOgotcbwiaor2yxZ2wIDAQAB
-AoGBAPCgMpmLxzwDaUmcFbTJUvlLW1hoxNNYSu2jIZm1k/hRAcE60JYwvBkgz3UB
-yMEh0AtLxYe0bFk6EHah11tMUPgscbCq73snJ++8koUw+csk22G65hOs51bVb7Aa
-6JBe67oLzdtvgCUFAA2qfrKzWRZzAdhUirQUZgySZk+Xq1pBAkEA/kZG0A6roTSM
-BVnx7LnPfsycKUsTumorpXiylZJjTi9XtmzxhrYN6wgZlDOOwOLgSQhszGpxVoMD
-u3gByT1b2QJBAPtL3mSKdvwRu/+40zaZLwvSJRxaj0mcE4BJOS6Oqs/hS1xRlrNk
-PpQ7WJ4yM6ZOLnXzm2mKyxm50Mv64109FtMCQQDOqS2KkjHaLowTGVxwC0DijMfr
-I9Lf8sSQk32J5VWCySWf5gGTfEnpmUa41gKTMJIbqZZLucNuDcOtzUaeWZlZAkA8
-ttXigLnCqR486JDPTi9ZscoZkZ+w7y6e/hH8t6d5Vjt48JVyfjPIaJY+km58LcN3
-6AWSeGAdtRFHVzR7oHjVAkB4hutvxiOeiIVQNBhM6RSI9aBPMI21DoX2JRoxvNW2
-cbvAhow217X9V0dVerEOKxnNYspXRrh36h7k4mQA+sDq
------END RSA PRIVATE KEY-----
-"""))
-
-cleartextCertificateRequestPEM = b("""-----BEGIN CERTIFICATE REQUEST-----
-MIIBnjCCAQcCAQAwXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQH
-EwdDaGljYWdvMRcwFQYDVQQKEw5NeSBDb21wYW55IEx0ZDEXMBUGA1UEAxMORnJl
-ZGVyaWNrIERlYW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANp6Y17WzKSw
-BsUWkXdqg6tnXy8H8hA1msCMWpc+/2KJ4mbv5NyD6UD+/SqagQqulPbF/DFea9nA
-E0zhmHJELcM8gUTIlXv/cgDWnmK4xj8YkjVUiCdqKRAKeuzLG1pGmwwF5lGeJpXN
-xQn5ecR0UYSOWj6TTGXB9VyUMQzCClcBAgMBAAGgADANBgkqhkiG9w0BAQUFAAOB
-gQAAJGuF/R/GGbeC7FbFW+aJgr9ee0Xbl6nlhu7pTe67k+iiKT2dsl2ti68MVTnu
-Vrb3HUNqOkiwsJf6kCtq5oPn3QVYzTa76Dt2y3Rtzv6boRSlmlfrgS92GNma8JfR
-oICQk3nAudi6zl1Dix3BCv1pUp5KMtGn3MeDEi6QFGy2rA==
------END CERTIFICATE REQUEST-----
-""")
-
-encryptedPrivateKeyPEM = b("""-----BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: DES-EDE3-CBC,9573604A18579E9E
-
-SHOho56WxDkT0ht10UTeKc0F5u8cqIa01kzFAmETw0MAs8ezYtK15NPdCXUm3X/2
-a17G7LSF5bkxOgZ7vpXyMzun/owrj7CzvLxyncyEFZWvtvzaAhPhvTJtTIB3kf8B
-8+qRcpTGK7NgXEgYBW5bj1y4qZkD4zCL9o9NQzsKI3Ie8i0239jsDOWR38AxjXBH
-mGwAQ4Z6ZN5dnmM4fhMIWsmFf19sNyAML4gHenQCHhmXbjXeVq47aC2ProInJbrm
-+00TcisbAQ40V9aehVbcDKtS4ZbMVDwncAjpXpcncC54G76N6j7F7wL7L/FuXa3A
-fvSVy9n2VfF/pJ3kYSflLHH2G/DFxjF7dl0GxhKPxJjp3IJi9VtuvmN9R2jZWLQF
-tfC8dXgy/P9CfFQhlinqBTEwgH0oZ/d4k4NVFDSdEMaSdmBAjlHpc+Vfdty3HVnV
-rKXj//wslsFNm9kIwJGIgKUa/n2jsOiydrsk1mgH7SmNCb3YHgZhbbnq0qLat/HC
-gHDt3FHpNQ31QzzL3yrenFB2L9osIsnRsDTPFNi4RX4SpDgNroxOQmyzCCV6H+d4
-o1mcnNiZSdxLZxVKccq0AfRpHqpPAFnJcQHP6xyT9MZp6fBa0XkxDnt9kNU8H3Qw
-7SJWZ69VXjBUzMlQViLuaWMgTnL+ZVyFZf9hTF7U/ef4HMLMAVNdiaGG+G+AjCV/
-MbzjS007Oe4qqBnCWaFPSnJX6uLApeTbqAxAeyCql56ULW5x6vDMNC3dwjvS/CEh
-11n8RkgFIQA0AhuKSIg3CbuartRsJnWOLwgLTzsrKYL4yRog1RJrtw==
------END RSA PRIVATE KEY-----
-""")
-
-encryptedPrivateKeyPEMPassphrase = b("foobar")
-
-# Some PKCS#7 stuff. Generated with the openssl command line:
-#
-# openssl crl2pkcs7 -inform pem -outform pem -certfile s.pem -nocrl
-#
-# with a certificate and key (but the key should be irrelevant) in s.pem
-pkcs7Data = b("""\
------BEGIN PKCS7-----
-MIIDNwYJKoZIhvcNAQcCoIIDKDCCAyQCAQExADALBgkqhkiG9w0BBwGgggMKMIID
-BjCCAm+gAwIBAgIBATANBgkqhkiG9w0BAQQFADB7MQswCQYDVQQGEwJTRzERMA8G
-A1UEChMITTJDcnlwdG8xFDASBgNVBAsTC00yQ3J5cHRvIENBMSQwIgYDVQQDExtN
-MkNyeXB0byBDZXJ0aWZpY2F0ZSBNYXN0ZXIxHTAbBgkqhkiG9w0BCQEWDm5ncHNA
-cG9zdDEuY29tMB4XDTAwMDkxMDA5NTEzMFoXDTAyMDkxMDA5NTEzMFowUzELMAkG
-A1UEBhMCU0cxETAPBgNVBAoTCE0yQ3J5cHRvMRIwEAYDVQQDEwlsb2NhbGhvc3Qx
-HTAbBgkqhkiG9w0BCQEWDm5ncHNAcG9zdDEuY29tMFwwDQYJKoZIhvcNAQEBBQAD
-SwAwSAJBAKy+e3dulvXzV7zoTZWc5TzgApr8DmeQHTYC8ydfzH7EECe4R1Xh5kwI
-zOuuFfn178FBiS84gngaNcrFi0Z5fAkCAwEAAaOCAQQwggEAMAkGA1UdEwQCMAAw
-LAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0G
-A1UdDgQWBBTPhIKSvnsmYsBVNWjj0m3M2z0qVTCBpQYDVR0jBIGdMIGagBT7hyNp
-65w6kxXlxb8pUU/+7Sg4AaF/pH0wezELMAkGA1UEBhMCU0cxETAPBgNVBAoTCE0y
-Q3J5cHRvMRQwEgYDVQQLEwtNMkNyeXB0byBDQTEkMCIGA1UEAxMbTTJDcnlwdG8g
-Q2VydGlmaWNhdGUgTWFzdGVyMR0wGwYJKoZIhvcNAQkBFg5uZ3BzQHBvc3QxLmNv
-bYIBADANBgkqhkiG9w0BAQQFAAOBgQA7/CqT6PoHycTdhEStWNZde7M/2Yc6BoJu
-VwnW8YxGO8Sn6UJ4FeffZNcYZddSDKosw8LtPOeWoK3JINjAk5jiPQ2cww++7QGG
-/g5NDjxFZNDJP1dGiLAxPW6JXwov4v0FmdzfLOZ01jDcgQQZqEpYlgpuI5JEWUQ9
-Ho4EzbYCOaEAMQA=
------END PKCS7-----
-""")
-
-crlData = b("""\
------BEGIN X509 CRL-----
-MIIBWzCBxTANBgkqhkiG9w0BAQQFADBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMC
-SUwxEDAOBgNVBAcTB0NoaWNhZ28xEDAOBgNVBAoTB1Rlc3RpbmcxGDAWBgNVBAMT
-D1Rlc3RpbmcgUm9vdCBDQRcNMDkwNzI2MDQzNDU2WhcNMTIwOTI3MDI0MTUyWjA8
-MBUCAgOrGA8yMDA5MDcyNTIzMzQ1NlowIwICAQAYDzIwMDkwNzI1MjMzNDU2WjAM
-MAoGA1UdFQQDCgEEMA0GCSqGSIb3DQEBBAUAA4GBAEBt7xTs2htdD3d4ErrcGAw1
-4dKcVnIWTutoI7xxen26Wwvh8VCsT7i/UeP+rBl9rC/kfjWjzQk3/zleaarGTpBT
-0yp4HXRFFoRhhSE/hP+eteaPXRgrsNRLHe9ZDd69wmh7J1wMDb0m81RG7kqcbsid
-vrzEeLDRiiPl92dyyWmu
------END X509 CRL-----
-""")
-
-
-# A broken RSA private key which can be used to test the error path through
-# PKey.check.
-inconsistentPrivateKeyPEM = b("""-----BEGIN RSA PRIVATE KEY-----
-MIIBPAIBAAJBAKy+e3dulvXzV7zoTZWc5TzgApr8DmeQHTYC8ydfzH7EECe4R1Xh
-5kwIzOuuFfn178FBiS84gngaNcrFi0Z5fAkCAwEaAQJBAIqm/bz4NA1H++Vx5Ewx
-OcKp3w19QSaZAwlGRtsUxrP7436QjnREM3Bm8ygU11BjkPVmtrKm6AayQfCHqJoT
-zIECIQDW0BoMoL0HOYM/mrTLhaykYAVqgIeJsPjvkEhTFXWBuQIhAM3deFAvWNu4
-nklUQ37XsCT2c9tmNt1LAT+slG2JOTTRAiAuXDtC/m3NYVwyHfFm+zKHRzHkClk2
-HjubeEgjpj32AQIhAJqMGTaZVOwevTXvvHwNeH+vRWsAYU/gbx+OQB+7VOcBAiEA
-oolb6NMg/R3enNPvS1O4UU1H8wpaF77L4yiSWlE0p4w=
------END RSA PRIVATE KEY-----
-""")
-
-
-class X509ExtTests(TestCase):
- """
- Tests for L{OpenSSL.crypto.X509Extension}.
- """
-
- def setUp(self):
- """
- Create a new private key and start a certificate request (for a test
- method to finish in one way or another).
- """
- # Basic setup stuff to generate a certificate
- self.pkey = PKey()
- self.pkey.generate_key(TYPE_RSA, 384)
- self.req = X509Req()
- self.req.set_pubkey(self.pkey)
- # Authority good you have.
- self.req.get_subject().commonName = "Yoda root CA"
- self.x509 = X509()
- self.subject = self.x509.get_subject()
- self.subject.commonName = self.req.get_subject().commonName
- self.x509.set_issuer(self.subject)
- self.x509.set_pubkey(self.pkey)
- now = b(datetime.now().strftime("%Y%m%d%H%M%SZ"))
- expire = b((datetime.now() + timedelta(days=100)).strftime("%Y%m%d%H%M%SZ"))
- self.x509.set_notBefore(now)
- self.x509.set_notAfter(expire)
-
-
- def test_str(self):
- """
- The string representation of L{X509Extension} instances as returned by
- C{str} includes stuff.
- """
- # This isn't necessarily the best string representation. Perhaps it
- # will be changed/improved in the future.
- self.assertEquals(
- str(X509Extension(b('basicConstraints'), True, b('CA:false'))),
- 'CA:FALSE')
-
-
- def test_type(self):
- """
- L{X509Extension} and L{X509ExtensionType} refer to the same type object
- and can be used to create instances of that type.
- """
- self.assertIdentical(X509Extension, X509ExtensionType)
- self.assertConsistentType(
- X509Extension,
- 'X509Extension', b('basicConstraints'), True, b('CA:true'))
-
-
- def test_construction(self):
- """
- L{X509Extension} accepts an extension type name, a critical flag,
- and an extension value and returns an L{X509ExtensionType} instance.
- """
- basic = X509Extension(b('basicConstraints'), True, b('CA:true'))
- self.assertTrue(
- isinstance(basic, X509ExtensionType),
- "%r is of type %r, should be %r" % (
- basic, type(basic), X509ExtensionType))
-
- comment = X509Extension(
- b('nsComment'), False, b('pyOpenSSL unit test'))
- self.assertTrue(
- isinstance(comment, X509ExtensionType),
- "%r is of type %r, should be %r" % (
- comment, type(comment), X509ExtensionType))
-
-
- def test_invalid_extension(self):
- """
- L{X509Extension} raises something if it is passed a bad extension
- name or value.
- """
- self.assertRaises(
- Error, X509Extension, b('thisIsMadeUp'), False, b('hi'))
- self.assertRaises(
- Error, X509Extension, b('basicConstraints'), False, b('blah blah'))
-
- # Exercise a weird one (an extension which uses the r2i method). This
- # exercises the codepath that requires a non-NULL ctx to be passed to
- # X509V3_EXT_nconf. It can't work now because we provide no
- # configuration database. It might be made to work in the future.
- self.assertRaises(
- Error, X509Extension, b('proxyCertInfo'), True,
- b('language:id-ppl-anyLanguage,pathlen:1,policy:text:AB'))
-
-
- def test_get_critical(self):
- """
- L{X509ExtensionType.get_critical} returns the value of the
- extension's critical flag.
- """
- ext = X509Extension(b('basicConstraints'), True, b('CA:true'))
- self.assertTrue(ext.get_critical())
- ext = X509Extension(b('basicConstraints'), False, b('CA:true'))
- self.assertFalse(ext.get_critical())
-
-
- def test_get_short_name(self):
- """
- L{X509ExtensionType.get_short_name} returns a string giving the short
- type name of the extension.
- """
- ext = X509Extension(b('basicConstraints'), True, b('CA:true'))
- self.assertEqual(ext.get_short_name(), b('basicConstraints'))
- ext = X509Extension(b('nsComment'), True, b('foo bar'))
- self.assertEqual(ext.get_short_name(), b('nsComment'))
-
-
- def test_get_data(self):
- """
- L{X509Extension.get_data} returns a string giving the data of the
- extension.
- """
- ext = X509Extension(b('basicConstraints'), True, b('CA:true'))
- # Expect to get back the DER encoded form of CA:true.
- self.assertEqual(ext.get_data(), b('0\x03\x01\x01\xff'))
-
-
- def test_get_data_wrong_args(self):
- """
- L{X509Extension.get_data} raises L{TypeError} if passed any arguments.
- """
- ext = X509Extension(b('basicConstraints'), True, b('CA:true'))
- self.assertRaises(TypeError, ext.get_data, None)
- self.assertRaises(TypeError, ext.get_data, "foo")
- self.assertRaises(TypeError, ext.get_data, 7)
-
-
- def test_unused_subject(self):
- """
- The C{subject} parameter to L{X509Extension} may be provided for an
- extension which does not use it and is ignored in this case.
- """
- ext1 = X509Extension(
- b('basicConstraints'), False, b('CA:TRUE'), subject=self.x509)
- self.x509.add_extensions([ext1])
- self.x509.sign(self.pkey, 'sha1')
- # This is a little lame. Can we think of a better way?
- text = dump_certificate(FILETYPE_TEXT, self.x509)
- self.assertTrue(b('X509v3 Basic Constraints:') in text)
- self.assertTrue(b('CA:TRUE') in text)
-
-
- def test_subject(self):
- """
- If an extension requires a subject, the C{subject} parameter to
- L{X509Extension} provides its value.
- """
- ext3 = X509Extension(
- b('subjectKeyIdentifier'), False, b('hash'), subject=self.x509)
- self.x509.add_extensions([ext3])
- self.x509.sign(self.pkey, 'sha1')
- text = dump_certificate(FILETYPE_TEXT, self.x509)
- self.assertTrue(b('X509v3 Subject Key Identifier:') in text)
-
-
- def test_missing_subject(self):
- """
- If an extension requires a subject and the C{subject} parameter is
- given no value, something happens.
- """
- self.assertRaises(
- Error, X509Extension, b('subjectKeyIdentifier'), False, b('hash'))
-
-
- 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.
- """
- for badObj in [True, object(), "hello", [], self]:
- self.assertRaises(
- TypeError,
- X509Extension,
- 'basicConstraints', False, 'CA:TRUE', subject=badObj)
-
-
- def test_unused_issuer(self):
- """
- The C{issuer} parameter to L{X509Extension} may be provided for an
- extension which does not use it and is ignored in this case.
- """
- ext1 = X509Extension(
- b('basicConstraints'), False, b('CA:TRUE'), issuer=self.x509)
- self.x509.add_extensions([ext1])
- self.x509.sign(self.pkey, 'sha1')
- text = dump_certificate(FILETYPE_TEXT, self.x509)
- self.assertTrue(b('X509v3 Basic Constraints:') in text)
- self.assertTrue(b('CA:TRUE') in text)
-
-
- def test_issuer(self):
- """
- If an extension requires a issuer, the C{issuer} parameter to
- L{X509Extension} provides its value.
- """
- ext2 = X509Extension(
- b('authorityKeyIdentifier'), False, b('issuer:always'),
- issuer=self.x509)
- self.x509.add_extensions([ext2])
- self.x509.sign(self.pkey, 'sha1')
- text = dump_certificate(FILETYPE_TEXT, self.x509)
- self.assertTrue(b('X509v3 Authority Key Identifier:') in text)
- self.assertTrue(b('DirName:/CN=Yoda root CA') in text)
-
-
- def test_missing_issuer(self):
- """
- If an extension requires an issue and the C{issuer} parameter is given
- no value, something happens.
- """
- self.assertRaises(
- Error,
- X509Extension,
- b('authorityKeyIdentifier'), False,
- b('keyid:always,issuer:always'))
-
-
- 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.
- """
- for badObj in [True, object(), "hello", [], self]:
- self.assertRaises(
- TypeError,
- X509Extension,
- 'authorityKeyIdentifier', False, 'keyid:always,issuer:always',
- issuer=badObj)
-
-
-
-class PKeyTests(TestCase):
- """
- Unit tests for L{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.
- """
- self.assertIdentical(PKey, PKeyType)
- self.assertConsistentType(PKey, 'PKey')
-
-
- def test_construction(self):
- """
- L{PKey} takes no arguments and returns a new L{PKey} instance.
- """
- self.assertRaises(TypeError, PKey, None)
- key = PKey()
- self.assertTrue(
- isinstance(key, PKeyType),
- "%r is of type %r, should be %r" % (key, type(key), PKeyType))
-
-
- 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
- generated.
- """
- key = PKey()
- self.assertEqual(key.type(), 0)
- self.assertEqual(key.bits(), 0)
- self.assertRaises(TypeError, key.check)
-
-
- 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
- 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.
- """
- key = PKey()
- self.assertRaises(TypeError, key.generate_key)
- self.assertRaises(TypeError, key.generate_key, 1, 2, 3)
- self.assertRaises(TypeError, key.generate_key, "foo", "bar")
- self.assertRaises(Error, key.generate_key, -1, 0)
-
- self.assertRaises(ValueError, key.generate_key, TYPE_RSA, -1)
- self.assertRaises(ValueError, key.generate_key, TYPE_RSA, 0)
-
- # XXX RSA generation for small values of bits is fairly buggy in a wide
- # range of OpenSSL versions. I need to figure out what the safe lower
- # bound for a reasonable number of OpenSSL versions is and explicitly
- # check for that in the wrapper. The failure behavior is typically an
- # infinite loop inside OpenSSL.
-
- # self.assertRaises(Error, key.generate_key, TYPE_RSA, 2)
-
- # XXX DSA generation seems happy with any number of bits. The DSS
- # says bits must be between 512 and 1024 inclusive. OpenSSL's DSA
- # generator doesn't seem to care about the upper limit at all. For
- # the lower limit, it uses 512 if anything smaller is specified.
- # So, it doesn't seem possible to make generate_key fail for
- # TYPE_DSA with a bits argument which is at least an int.
-
- # self.assertRaises(Error, key.generate_key, TYPE_DSA, -7)
-
-
- 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.
- """
- bits = 128
- key = PKey()
- key.generate_key(TYPE_RSA, bits)
- self.assertEqual(key.type(), TYPE_RSA)
- self.assertEqual(key.bits(), bits)
- self.assertTrue(key.check())
-
-
- 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.
- """
- # 512 is a magic number. The DSS (Digital Signature Standard)
- # allows a minimum of 512 bits for DSA. DSA_generate_parameters
- # will silently promote any value below 512 to 512.
- 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)
-
-
- def test_regeneration(self):
- """
- L{PKeyType.generate_key} can be called multiple times on the same
- key to generate new keys.
- """
- key = PKey()
- for type, bits in [(TYPE_RSA, 512), (TYPE_DSA, 576)]:
- key.generate_key(type, bits)
- self.assertEqual(key.type(), type)
- self.assertEqual(key.bits(), bits)
-
-
- def test_inconsistentKey(self):
- """
- L{PKeyType.check} returns C{False} if the key is not consistent.
- """
- key = load_privatekey(FILETYPE_PEM, inconsistentPrivateKeyPEM)
- self.assertRaises(Error, key.check)
-
-
- def test_check_wrong_args(self):
- """
- L{PKeyType.check} raises L{TypeError} if called with any arguments.
- """
- self.assertRaises(TypeError, PKey().check, None)
- self.assertRaises(TypeError, PKey().check, object())
- self.assertRaises(TypeError, PKey().check, 1)
-
-
-
-class X509NameTests(TestCase):
- """
- Unit tests for L{OpenSSL.crypto.X509Name}.
- """
- def _x509name(self, **attrs):
- # XXX There's no other way to get a new X509Name yet.
- name = X509().get_subject()
- attrs = list(attrs.items())
- # Make the order stable - order matters!
- def key(attr):
- return attr[1]
- attrs.sort(key=key)
- for k, v in attrs:
- setattr(name, k, v)
- return name
-
-
- def test_type(self):
- """
- The type of X509Name objects is L{X509NameType}.
- """
- self.assertIdentical(X509Name, X509NameType)
- self.assertEqual(X509NameType.__name__, 'X509Name')
- self.assertTrue(isinstance(X509NameType, type))
-
- name = self._x509name()
- self.assertTrue(
- isinstance(name, X509NameType),
- "%r is of type %r, should be %r" % (
- name, type(name), X509NameType))
-
-
- def test_onlyStringAttributes(self):
- """
- Attempting to set a non-L{str} attribute name on an L{X509NameType}
- instance causes L{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.
- 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
- raised.
- """
- name = self._x509name()
- self.assertRaises(AttributeError, setattr, name, "no such thing", None)
-
-
- def test_attributes(self):
- """
- L{X509NameType} instances have attributes for each standard (?)
- X509Name field.
- """
- name = self._x509name()
- name.commonName = "foo"
- self.assertEqual(name.commonName, "foo")
- self.assertEqual(name.CN, "foo")
- name.CN = "baz"
- self.assertEqual(name.commonName, "baz")
- self.assertEqual(name.CN, "baz")
- name.commonName = "bar"
- self.assertEqual(name.commonName, "bar")
- self.assertEqual(name.CN, "bar")
- name.CN = "quux"
- self.assertEqual(name.commonName, "quux")
- self.assertEqual(name.CN, "quux")
-
-
- 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
- one.
- """
- name = self._x509name(commonName="foo", emailAddress="bar@example.com")
-
- copy = X509Name(name)
- self.assertEqual(copy.commonName, "foo")
- self.assertEqual(copy.emailAddress, "bar@example.com")
-
- # Mutate the copy and ensure the original is unmodified.
- copy.commonName = "baz"
- self.assertEqual(name.commonName, "foo")
-
- # Mutate the original and ensure the copy is unmodified.
- name.emailAddress = "quux@example.com"
- self.assertEqual(copy.emailAddress, "bar@example.com")
-
-
- def test_repr(self):
- """
- L{repr} passed an L{X509NameType} instance should return a string
- containing a description of the type and the NIDs which have been set
- on it.
- """
- name = self._x509name(commonName="foo", emailAddress="bar")
- self.assertEqual(
- repr(name),
- "<X509Name object '/emailAddress=bar/CN=foo'>")
-
-
- def test_comparison(self):
- """
- L{X509NameType} instances should compare based on their NIDs.
- """
- def _equality(a, b, assertTrue, assertFalse):
- assertTrue(a == b, "(%r == %r) --> False" % (a, b))
- assertFalse(a != b)
- assertTrue(b == a)
- assertFalse(b != a)
-
- def assertEqual(a, b):
- _equality(a, b, self.assertTrue, self.assertFalse)
-
- # Instances compare equal to themselves.
- name = self._x509name()
- assertEqual(name, name)
-
- # Empty instances should compare equal to each other.
- assertEqual(self._x509name(), self._x509name())
-
- # Instances with equal NIDs should compare equal to each other.
- assertEqual(self._x509name(commonName="foo"),
- self._x509name(commonName="foo"))
-
- # Instance with equal NIDs set using different aliases should compare
- # equal to each other.
- assertEqual(self._x509name(commonName="foo"),
- self._x509name(CN="foo"))
-
- # Instances with more than one NID with the same values should compare
- # equal to each other.
- assertEqual(self._x509name(CN="foo", organizationalUnitName="bar"),
- self._x509name(commonName="foo", OU="bar"))
-
- def assertNotEqual(a, b):
- _equality(a, b, self.assertFalse, self.assertTrue)
-
- # Instances with different values for the same NID should not compare
- # equal to each other.
- assertNotEqual(self._x509name(CN="foo"),
- self._x509name(CN="bar"))
-
- # Instances with different NIDs should not compare equal to each other.
- assertNotEqual(self._x509name(CN="foo"),
- self._x509name(OU="foo"))
-
- def _inequality(a, b, assertTrue, assertFalse):
- assertTrue(a < b)
- assertTrue(a <= b)
- assertTrue(b > a)
- assertTrue(b >= a)
- assertFalse(a > b)
- assertFalse(a >= b)
- assertFalse(b < a)
- assertFalse(b <= a)
-
- def assertLessThan(a, b):
- _inequality(a, b, self.assertTrue, self.assertFalse)
-
- # An X509Name with a NID with a value which sorts less than the value
- # of the same NID on another X509Name compares less than the other
- # X509Name.
- assertLessThan(self._x509name(CN="abc"),
- self._x509name(CN="def"))
-
- def assertGreaterThan(a, b):
- _inequality(a, b, self.assertFalse, self.assertTrue)
-
- # An X509Name with a NID with a value which sorts greater than the
- # value of the same NID on another X509Name compares greater than the
- # other X509Name.
- assertGreaterThan(self._x509name(CN="def"),
- self._x509name(CN="abc"))
-
-
- def test_hash(self):
- """
- L{X509Name.hash} returns an integer hash based on the value of the
- name.
- """
- a = self._x509name(CN="foo")
- b = self._x509name(CN="foo")
- self.assertEqual(a.hash(), b.hash())
- a.CN = "bar"
- self.assertNotEqual(a.hash(), b.hash())
-
-
- def test_der(self):
- """
- L{X509Name.der} returns the DER encoded form of the name.
- """
- a = self._x509name(CN="foo", C="US")
- self.assertEqual(
- a.der(),
- b('0\x1b1\x0b0\t\x06\x03U\x04\x06\x13\x02US'
- '1\x0c0\n\x06\x03U\x04\x03\x13\x03foo'))
-
-
- def test_get_components(self):
- """
- L{X509Name.get_components} returns a C{list} of two-tuples of C{str}
- giving the NIDs and associated values which make up the name.
- """
- a = self._x509name()
- self.assertEqual(a.get_components(), [])
- a.CN = "foo"
- self.assertEqual(a.get_components(), [(b("CN"), b("foo"))])
- a.organizationalUnitName = "bar"
- self.assertEqual(
- a.get_components(),
- [(b("CN"), b("foo")), (b("OU"), b("bar"))])
-
-
-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.
- """
- raise NotImplementedError()
-
-
- def test_signWithUngenerated(self):
- """
- L{X509Req.sign} raises L{ValueError} when pass a L{PKey} with no parts.
- """
- request = self.signable()
- key = PKey()
- self.assertRaises(ValueError, request.sign, key, 'MD5')
-
-
- def test_signWithPublicKey(self):
- """
- L{X509Req.sign} raises L{ValueError} when pass a L{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')
-
-
- def test_signWithUnknownDigest(self):
- """
- L{X509Req.sign} raises L{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")
-
-
- 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.
- """
- request = self.signable()
- key = PKey()
- key.generate_key(TYPE_RSA, 512)
- request.set_pubkey(key)
- request.sign(key, 'MD5')
- # If the type has a verify method, cover that too.
- if getattr(request, 'verify', None) is not None:
- pub = request.get_pubkey()
- self.assertTrue(request.verify(pub))
- # Make another key that won't verify.
- key = PKey()
- key.generate_key(TYPE_RSA, 512)
- self.assertRaises(Error, request.verify, key)
-
-
-
-
-class X509ReqTests(TestCase, _PKeyInteractionTestsMixin):
- """
- Tests for L{OpenSSL.crypto.X509Req}.
- """
- def signable(self):
- """
- Create and return a new L{X509Req}.
- """
- return X509Req()
-
-
- def test_type(self):
- """
- L{X509Req} and L{X509ReqType} refer to the same type object and can be
- used to create instances of that type.
- """
- self.assertIdentical(X509Req, X509ReqType)
- self.assertConsistentType(X509Req, 'X509Req')
-
-
- def test_construction(self):
- """
- L{X509Req} takes no arguments and returns an L{X509ReqType} instance.
- """
- request = X509Req()
- self.assertTrue(
- isinstance(request, X509ReqType),
- "%r is of type %r, should be %r" % (request, type(request), X509ReqType))
-
-
- 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
- the certificate request. The initial value of the version is 0.
- """
- request = X509Req()
- self.assertEqual(request.get_version(), 0)
- request.set_version(1)
- self.assertEqual(request.get_version(), 1)
- request.set_version(3)
- self.assertEqual(request.get_version(), 3)
-
-
- 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
- arguments.
- """
- request = X509Req()
- self.assertRaises(TypeError, request.set_version)
- self.assertRaises(TypeError, request.set_version, "foo")
- self.assertRaises(TypeError, request.set_version, 1, 2)
- self.assertRaises(TypeError, request.get_version, None)
-
-
- def test_get_subject(self):
- """
- L{X509ReqType.get_subject} returns an L{X509Name} for the subject of
- the request and which is valid even after the request object is
- otherwise dead.
- """
- request = X509Req()
- subject = request.get_subject()
- self.assertTrue(
- isinstance(subject, X509NameType),
- "%r is of type %r, should be %r" % (subject, type(subject), X509NameType))
- subject.commonName = "foo"
- self.assertEqual(request.get_subject().commonName, "foo")
- del request
- subject.commonName = "bar"
- self.assertEqual(subject.commonName, "bar")
-
-
- def test_get_subject_wrong_args(self):
- """
- L{X509ReqType.get_subject} raises L{TypeError} if called with any
- arguments.
- """
- request = X509Req()
- self.assertRaises(TypeError, request.get_subject, None)
-
-
- def test_add_extensions(self):
- """
- L{X509Req.add_extensions} accepts a C{list} of L{X509Extension}
- instances and adds them to the X509 request.
- """
- request = X509Req()
- request.add_extensions([
- X509Extension(b('basicConstraints'), True, b('CA:false'))])
- # XXX Add get_extensions so the rest of this unit test can be written.
-
-
- 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}
- instances.
- """
- request = X509Req()
- self.assertRaises(TypeError, request.add_extensions)
- self.assertRaises(TypeError, request.add_extensions, object())
- self.assertRaises(ValueError, request.add_extensions, [object()])
- self.assertRaises(TypeError, request.add_extensions, [], None)
-
-
-
-class X509Tests(TestCase, _PKeyInteractionTestsMixin):
- """
- Tests for L{OpenSSL.crypto.X509}.
- """
- pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
-
- extpem = """
------BEGIN CERTIFICATE-----
-MIIC3jCCAkegAwIBAgIJAJHFjlcCgnQzMA0GCSqGSIb3DQEBBQUAMEcxCzAJBgNV
-BAYTAlNFMRUwEwYDVQQIEwxXZXN0ZXJib3R0b20xEjAQBgNVBAoTCUNhdGFsb2dp
-eDENMAsGA1UEAxMEUm9vdDAeFw0wODA0MjIxNDQ1MzhaFw0wOTA0MjIxNDQ1Mzha
-MFQxCzAJBgNVBAYTAlNFMQswCQYDVQQIEwJXQjEUMBIGA1UEChMLT3Blbk1ldGFk
-aXIxIjAgBgNVBAMTGW5vZGUxLm9tMi5vcGVubWV0YWRpci5vcmcwgZ8wDQYJKoZI
-hvcNAQEBBQADgY0AMIGJAoGBAPIcQMrwbk2nESF/0JKibj9i1x95XYAOwP+LarwT
-Op4EQbdlI9SY+uqYqlERhF19w7CS+S6oyqx0DRZSk4Y9dZ9j9/xgm2u/f136YS1u
-zgYFPvfUs6PqYLPSM8Bw+SjJ+7+2+TN+Tkiof9WP1cMjodQwOmdsiRbR0/J7+b1B
-hec1AgMBAAGjgcQwgcEwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNT
-TCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFIdHsBcMVVMbAO7j6NCj
-03HgLnHaMB8GA1UdIwQYMBaAFL2h9Bf9Mre4vTdOiHTGAt7BRY/8MEYGA1UdEQQ/
-MD2CDSouZXhhbXBsZS5vcmeCESoub20yLmV4bWFwbGUuY29thwSC7wgKgRNvbTJA
-b3Blbm1ldGFkaXIub3JnMA0GCSqGSIb3DQEBBQUAA4GBALd7WdXkp2KvZ7/PuWZA
-MPlIxyjS+Ly11+BNE0xGQRp9Wz+2lABtpgNqssvU156+HkKd02rGheb2tj7MX9hG
-uZzbwDAZzJPjzDQDD7d3cWsrVcfIdqVU7epHqIadnOF+X0ghJ39pAm6VVadnSXCt
-WpOdIpB8KksUTCzV591Nr1wd
------END CERTIFICATE-----
- """
- def signable(self):
- """
- Create and return a new L{X509}.
- """
- return X509()
-
-
- def test_type(self):
- """
- L{X509} and L{X509Type} refer to the same type object and can be used
- to create instances of that type.
- """
- self.assertIdentical(X509, X509Type)
- self.assertConsistentType(X509, 'X509')
-
-
- def test_construction(self):
- """
- L{X509} takes no arguments and returns an instance of L{X509Type}.
- """
- certificate = X509()
- self.assertTrue(
- isinstance(certificate, X509Type),
- "%r is of type %r, should be %r" % (certificate,
- type(certificate),
- X509Type))
- self.assertEqual(type(X509Type).__name__, 'type')
- self.assertEqual(type(certificate).__name__, 'X509')
- self.assertEqual(type(certificate), X509Type)
- self.assertEqual(type(certificate), X509)
-
-
- def test_get_version_wrong_args(self):
- """
- L{X509.get_version} raises L{TypeError} if invoked with any arguments.
- """
- cert = X509()
- self.assertRaises(TypeError, cert.get_version, None)
-
-
- 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}.
- """
- cert = X509()
- self.assertRaises(TypeError, cert.set_version)
- self.assertRaises(TypeError, cert.set_version, None)
- self.assertRaises(TypeError, cert.set_version, 1, None)
-
-
- def test_version(self):
- """
- L{X509.set_version} sets the certificate version number.
- L{X509.get_version} retrieves it.
- """
- cert = X509()
- cert.set_version(1234)
- self.assertEquals(cert.get_version(), 1234)
-
-
- def test_get_serial_number_wrong_args(self):
- """
- L{X509.get_serial_number} raises L{TypeError} if invoked with any
- arguments.
- """
- cert = X509()
- self.assertRaises(TypeError, cert.get_serial_number, None)
-
-
- 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}.
- """
- certificate = X509()
- self.assertRaises(TypeError, certificate.set_serial_number)
- self.assertRaises(TypeError, certificate.set_serial_number, 1, 2)
- self.assertRaises(TypeError, certificate.set_serial_number, "1")
- self.assertRaises(TypeError, certificate.set_serial_number, 5.5)
- self.assertEqual(certificate.get_serial_number(), 0)
- certificate.set_serial_number(1)
- self.assertEqual(certificate.get_serial_number(), 1)
- certificate.set_serial_number(2 ** 32 + 1)
- self.assertEqual(certificate.get_serial_number(), 2 ** 32 + 1)
- certificate.set_serial_number(2 ** 64 + 1)
- self.assertEqual(certificate.get_serial_number(), 2 ** 64 + 1)
- certificate.set_serial_number(2 ** 128 + 1)
- self.assertEqual(certificate.get_serial_number(), 2 ** 128 + 1)
-
-
- def _setBoundTest(self, which):
- """
- L{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.
- """
- certificate = X509()
- set = getattr(certificate, 'set_not' + which)
- get = getattr(certificate, 'get_not' + which)
-
- # Starts with no value.
- self.assertEqual(get(), None)
-
- # GMT (Or is it UTC?) -exarkun
- when = b("20040203040506Z")
- set(when)
- self.assertEqual(get(), when)
-
- # A plus two hours and thirty minutes offset
- when = b("20040203040506+0530")
- set(when)
- self.assertEqual(get(), when)
-
- # A minus one hour fifteen minutes offset
- when = b("20040203040506-0115")
- set(when)
- self.assertEqual(get(), when)
-
- # An invalid string results in a ValueError
- self.assertRaises(ValueError, set, b("foo bar"))
-
- # The wrong number of arguments results in a TypeError.
- self.assertRaises(TypeError, set)
- self.assertRaises(TypeError, set, b("20040203040506Z"), b("20040203040506Z"))
- self.assertRaises(TypeError, get, b("foo bar"))
-
-
- # XXX ASN1_TIME (not GENERALIZEDTIME)
-
- def test_set_notBefore(self):
- """
- L{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.
- """
- self._setBoundTest("Before")
-
-
- def test_set_notAfter(self):
- """
- L{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.
- """
- self._setBoundTest("After")
-
-
- def test_get_notBefore(self):
- """
- L{X509Type.get_notBefore} returns a string in the format of an ASN1
- GENERALIZEDTIME even for certificates which store it as UTCTIME
- internally.
- """
- cert = load_certificate(FILETYPE_PEM, self.pemData)
- self.assertEqual(cert.get_notBefore(), b("20090325123658Z"))
-
-
- def test_get_notAfter(self):
- """
- L{X509Type.get_notAfter} returns a string in the format of an ASN1
- GENERALIZEDTIME even for certificates which store it as UTCTIME
- internally.
- """
- cert = load_certificate(FILETYPE_PEM, self.pemData)
- self.assertEqual(cert.get_notAfter(), b("20170611123658Z"))
-
-
- 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.
- """
- cert = X509()
- self.assertRaises(TypeError, cert.gmtime_adj_notBefore)
- self.assertRaises(TypeError, cert.gmtime_adj_notBefore, None)
- self.assertRaises(TypeError, cert.gmtime_adj_notBefore, 123, None)
-
-
- def test_gmtime_adj_notBefore(self):
- """
- L{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)
- now = datetime.utcnow() + timedelta(seconds=100)
- cert.gmtime_adj_notBefore(100)
- self.assertEqual(cert.get_notBefore(), b(now.strftime("%Y%m%d%H%M%SZ")))
-
-
- 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.
- """
- cert = X509()
- self.assertRaises(TypeError, cert.gmtime_adj_notAfter)
- self.assertRaises(TypeError, cert.gmtime_adj_notAfter, None)
- self.assertRaises(TypeError, cert.gmtime_adj_notAfter, 123, None)
-
-
- def test_gmtime_adj_notAfter(self):
- """
- L{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)
- now = datetime.utcnow() + timedelta(seconds=100)
- cert.gmtime_adj_notAfter(100)
- self.assertEqual(cert.get_notAfter(), b(now.strftime("%Y%m%d%H%M%SZ")))
-
-
- def test_has_expired_wrong_args(self):
- """
- L{X509Type.has_expired} raises L{TypeError} if called with any
- arguments.
- """
- cert = X509()
- self.assertRaises(TypeError, cert.has_expired, None)
-
-
- def test_has_expired(self):
- """
- L{X509Type.has_expired} returns C{True} if the certificate's not-after
- time is in the past.
- """
- cert = X509()
- cert.gmtime_adj_notAfter(-1)
- self.assertTrue(cert.has_expired())
-
-
- def test_has_not_expired(self):
- """
- L{X509Type.has_expired} returns C{False} if the certificate's not-after
- time is in the future.
- """
- cert = X509()
- cert.gmtime_adj_notAfter(2)
- self.assertFalse(cert.has_expired())
-
-
- def test_digest(self):
- """
- L{X509.digest} returns a string giving ":"-separated hex-encoded words
- of the digest of the certificate.
- """
- cert = X509()
- self.assertEqual(
- cert.digest("md5"),
- b("A8:EB:07:F8:53:25:0A:F2:56:05:C5:A5:C4:C4:C7:15"))
-
-
- def _extcert(self, pkey, extensions):
- cert = X509()
- cert.set_pubkey(pkey)
- cert.get_subject().commonName = "Unit Tests"
- cert.get_issuer().commonName = "Unit Tests"
- when = b(datetime.now().strftime("%Y%m%d%H%M%SZ"))
- cert.set_notBefore(when)
- cert.set_notAfter(when)
-
- cert.add_extensions(extensions)
- return load_certificate(
- FILETYPE_PEM, dump_certificate(FILETYPE_PEM, cert))
-
-
- def test_extension_count(self):
- """
- L{X509.get_extension_count} returns the number of extensions that are
- present in the certificate.
- """
- pkey = load_privatekey(FILETYPE_PEM, client_key_pem)
- ca = X509Extension(b('basicConstraints'), True, b('CA:FALSE'))
- key = X509Extension(b('keyUsage'), True, b('digitalSignature'))
- subjectAltName = X509Extension(
- b('subjectAltName'), True, b('DNS:example.com'))
-
- # Try a certificate with no extensions at all.
- c = self._extcert(pkey, [])
- self.assertEqual(c.get_extension_count(), 0)
-
- # And a certificate with one
- c = self._extcert(pkey, [ca])
- self.assertEqual(c.get_extension_count(), 1)
-
- # And a certificate with several
- c = self._extcert(pkey, [ca, key, subjectAltName])
- self.assertEqual(c.get_extension_count(), 3)
-
-
- def test_get_extension(self):
- """
- L{X509.get_extension} takes an integer and returns an L{X509Extension}
- corresponding to the extension at that index.
- """
- pkey = load_privatekey(FILETYPE_PEM, client_key_pem)
- ca = X509Extension(b('basicConstraints'), True, b('CA:FALSE'))
- key = X509Extension(b('keyUsage'), True, b('digitalSignature'))
- subjectAltName = X509Extension(
- b('subjectAltName'), False, b('DNS:example.com'))
-
- cert = self._extcert(pkey, [ca, key, subjectAltName])
-
- ext = cert.get_extension(0)
- self.assertTrue(isinstance(ext, X509Extension))
- self.assertTrue(ext.get_critical())
- self.assertEqual(ext.get_short_name(), b('basicConstraints'))
-
- ext = cert.get_extension(1)
- self.assertTrue(isinstance(ext, X509Extension))
- self.assertTrue(ext.get_critical())
- self.assertEqual(ext.get_short_name(), b('keyUsage'))
-
- ext = cert.get_extension(2)
- self.assertTrue(isinstance(ext, X509Extension))
- self.assertFalse(ext.get_critical())
- self.assertEqual(ext.get_short_name(), b('subjectAltName'))
-
- self.assertRaises(IndexError, cert.get_extension, -1)
- self.assertRaises(IndexError, cert.get_extension, 4)
- self.assertRaises(TypeError, cert.get_extension, "hello")
-
-
- def test_invalid_digest_algorithm(self):
- """
- L{X509.digest} raises L{ValueError} if called with an unrecognized hash
- algorithm.
- """
- cert = X509()
- self.assertRaises(ValueError, cert.digest, "monkeys")
-
-
- def test_get_subject_wrong_args(self):
- """
- L{X509.get_subject} raises L{TypeError} if called with any arguments.
- """
- cert = X509()
- self.assertRaises(TypeError, cert.get_subject, None)
-
-
- def test_get_subject(self):
- """
- L{X509.get_subject} returns an L{X509Name} instance.
- """
- cert = load_certificate(FILETYPE_PEM, self.pemData)
- subj = cert.get_subject()
- self.assertTrue(isinstance(subj, X509Name))
- self.assertEquals(
- subj.get_components(),
- [(b('C'), b('US')), (b('ST'), b('IL')), (b('L'), b('Chicago')),
- (b('O'), b('Testing')), (b('CN'), b('Testing Root CA'))])
-
-
- 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}.
- """
- cert = X509()
- self.assertRaises(TypeError, cert.set_subject)
- self.assertRaises(TypeError, cert.set_subject, None)
- self.assertRaises(TypeError, cert.set_subject, cert.get_subject(), None)
-
-
- def test_set_subject(self):
- """
- L{X509.set_subject} changes the subject of the certificate to the one
- passed in.
- """
- cert = X509()
- name = cert.get_subject()
- name.C = 'AU'
- name.O = 'Unit Tests'
- cert.set_subject(name)
- self.assertEquals(
- cert.get_subject().get_components(),
- [(b('C'), b('AU')), (b('O'), b('Unit Tests'))])
-
-
- def test_get_issuer_wrong_args(self):
- """
- L{X509.get_issuer} raises L{TypeError} if called with any arguments.
- """
- cert = X509()
- self.assertRaises(TypeError, cert.get_issuer, None)
-
-
- def test_get_issuer(self):
- """
- L{X509.get_issuer} returns an L{X509Name} instance.
- """
- cert = load_certificate(FILETYPE_PEM, self.pemData)
- subj = cert.get_issuer()
- self.assertTrue(isinstance(subj, X509Name))
- comp = subj.get_components()
- self.assertEquals(
- comp,
- [(b('C'), b('US')), (b('ST'), b('IL')), (b('L'), b('Chicago')),
- (b('O'), b('Testing')), (b('CN'), b('Testing Root CA'))])
-
-
- 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}.
- """
- cert = X509()
- self.assertRaises(TypeError, cert.set_issuer)
- self.assertRaises(TypeError, cert.set_issuer, None)
- self.assertRaises(TypeError, cert.set_issuer, cert.get_issuer(), None)
-
-
- def test_set_issuer(self):
- """
- L{X509.set_issuer} changes the issuer of the certificate to the one
- passed in.
- """
- cert = X509()
- name = cert.get_issuer()
- name.C = 'AU'
- name.O = 'Unit Tests'
- cert.set_issuer(name)
- self.assertEquals(
- cert.get_issuer().get_components(),
- [(b('C'), b('AU')), (b('O'), b('Unit Tests'))])
-
-
- def test_get_pubkey_uninitialized(self):
- """
- When called on a certificate with no public key, L{X509.get_pubkey}
- raises L{OpenSSL.crypto.Error}.
- """
- cert = X509()
- self.assertRaises(Error, cert.get_pubkey)
-
-
- def test_subject_name_hash_wrong_args(self):
- """
- L{X509.subject_name_hash} raises L{TypeError} if called with any
- arguments.
- """
- cert = X509()
- self.assertRaises(TypeError, cert.subject_name_hash, None)
-
-
- def test_subject_name_hash(self):
- """
- L{X509.subject_name_hash} returns the hash of the certificate's subject
- name.
- """
- cert = load_certificate(FILETYPE_PEM, self.pemData)
- self.assertIn(
- cert.subject_name_hash(),
- [3350047874, # OpenSSL 0.9.8, MD5
- 3278919224, # OpenSSL 1.0.0, SHA1
- ])
-
-
- def test_get_signature_algorithm(self):
- """
- L{X509Type.get_signature_algorithm} returns a string which means
- the algorithm used to sign the certificate.
- """
- cert = load_certificate(FILETYPE_PEM, self.pemData)
- self.assertEqual(
- b("sha1WithRSAEncryption"), cert.get_signature_algorithm())
-
-
- def test_get_undefined_signature_algorithm(self):
- """
- L{X509Type.get_signature_algorithm} raises L{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 = """\
------BEGIN CERTIFICATE-----
-MIIC/zCCAmigAwIBAgIBATAGBgJ8BQUAMHsxCzAJBgNVBAYTAlNHMREwDwYDVQQK
-EwhNMkNyeXB0bzEUMBIGA1UECxMLTTJDcnlwdG8gQ0ExJDAiBgNVBAMTG00yQ3J5
-cHRvIENlcnRpZmljYXRlIE1hc3RlcjEdMBsGCSqGSIb3DQEJARYObmdwc0Bwb3N0
-MS5jb20wHhcNMDAwOTEwMDk1MTMwWhcNMDIwOTEwMDk1MTMwWjBTMQswCQYDVQQG
-EwJTRzERMA8GA1UEChMITTJDcnlwdG8xEjAQBgNVBAMTCWxvY2FsaG9zdDEdMBsG
-CSqGSIb3DQEJARYObmdwc0Bwb3N0MS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBI
-AkEArL57d26W9fNXvOhNlZzlPOACmvwOZ5AdNgLzJ1/MfsQQJ7hHVeHmTAjM664V
-+fXvwUGJLziCeBo1ysWLRnl8CQIDAQABo4IBBDCCAQAwCQYDVR0TBAIwADAsBglg
-hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O
-BBYEFM+EgpK+eyZiwFU1aOPSbczbPSpVMIGlBgNVHSMEgZ0wgZqAFPuHI2nrnDqT
-FeXFvylRT/7tKDgBoX+kfTB7MQswCQYDVQQGEwJTRzERMA8GA1UEChMITTJDcnlw
-dG8xFDASBgNVBAsTC00yQ3J5cHRvIENBMSQwIgYDVQQDExtNMkNyeXB0byBDZXJ0
-aWZpY2F0ZSBNYXN0ZXIxHTAbBgkqhkiG9w0BCQEWDm5ncHNAcG9zdDEuY29tggEA
-MA0GCSqGSIb3DQEBBAUAA4GBADv8KpPo+gfJxN2ERK1Y1l17sz/ZhzoGgm5XCdbx
-jEY7xKfpQngV599k1xhl11IMqizDwu0855agrckg2MCTmOI9DZzDD77tAYb+Dk0O
-PEVk0Mk/V0aIsDE9bolfCi/i/QWZ3N8s5nTWMNyBBBmoSliWCm4jkkRZRD0ejgTN
-tgI5
------END CERTIFICATE-----
-"""
- cert = load_certificate(FILETYPE_PEM, certPEM)
- self.assertRaises(ValueError, cert.get_signature_algorithm)
-
-
-
-class PKCS12Tests(TestCase):
- """
- Test for L{OpenSSL.crypto.PKCS12} and L{OpenSSL.crypto.load_pkcs12}.
- """
- pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
-
- def test_type(self):
- """
- L{PKCS12Type} is a type object.
- """
- self.assertIdentical(PKCS12, PKCS12Type)
- self.assertConsistentType(PKCS12, 'PKCS12')
-
-
- def test_empty_construction(self):
- """
- L{PKCS12} returns a new instance of L{PKCS12} with no certificate,
- private key, CA certificates, or friendly name.
- """
- p12 = PKCS12()
- self.assertEqual(None, p12.get_certificate())
- self.assertEqual(None, p12.get_privatekey())
- self.assertEqual(None, p12.get_ca_certificates())
- self.assertEqual(None, p12.get_friendlyname())
-
-
- 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}
- when passed objects of types other than those expected.
- """
- p12 = PKCS12()
- self.assertRaises(TypeError, p12.set_certificate, 3)
- self.assertRaises(TypeError, p12.set_certificate, PKey())
- self.assertRaises(TypeError, p12.set_certificate, X509)
- self.assertRaises(TypeError, p12.set_privatekey, 3)
- self.assertRaises(TypeError, p12.set_privatekey, 'legbone')
- self.assertRaises(TypeError, p12.set_privatekey, X509())
- self.assertRaises(TypeError, p12.set_ca_certificates, 3)
- self.assertRaises(TypeError, p12.set_ca_certificates, X509())
- self.assertRaises(TypeError, p12.set_ca_certificates, (3, 4))
- self.assertRaises(TypeError, p12.set_ca_certificates, ( PKey(), ))
- self.assertRaises(TypeError, p12.set_friendlyname, 6)
- self.assertRaises(TypeError, p12.set_friendlyname, ('foo', 'bar'))
-
-
- 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}.
- """
- passwd = 'blah'
- p12 = PKCS12()
- pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
- p12.set_privatekey(pkey)
- self.assertEqual(None, p12.get_certificate())
- self.assertEqual(pkey, p12.get_privatekey())
- try:
- dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3)
- except Error:
- # Some versions of OpenSSL will throw an exception
- # for this nearly useless PKCS12 we tried to generate:
- # [('PKCS12 routines', 'PKCS12_create', 'invalid null argument')]
- return
- p12 = load_pkcs12(dumped_p12, passwd)
- self.assertEqual(None, p12.get_ca_certificates())
- self.assertEqual(None, p12.get_certificate())
-
- # OpenSSL fails to bring the key back to us. So sad. Perhaps in the
- # future this will be improved.
- self.assertTrue(isinstance(p12.get_privatekey(), (PKey, type(None))))
-
-
- 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}.
- """
- passwd = 'blah'
- p12 = PKCS12()
- cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
- p12.set_certificate(cert)
- self.assertEqual(cert, p12.get_certificate())
- self.assertEqual(None, p12.get_privatekey())
- try:
- dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3)
- except Error:
- # Some versions of OpenSSL will throw an exception
- # for this nearly useless PKCS12 we tried to generate:
- # [('PKCS12 routines', 'PKCS12_create', 'invalid null argument')]
- return
- p12 = load_pkcs12(dumped_p12, passwd)
- self.assertEqual(None, p12.get_privatekey())
-
- # OpenSSL fails to bring the cert back to us. Groany mcgroan.
- self.assertTrue(isinstance(p12.get_certificate(), (X509, type(None))))
-
- # Oh ho. It puts the certificate into the ca certificates list, in
- # fact. Totally bogus, I would think. Nevertheless, let's exploit
- # that to check to see if it reconstructed the certificate we expected
- # it to. At some point, hopefully this will change so that
- # p12.get_certificate() is actually what returns the loaded
- # certificate.
- self.assertEqual(
- cleartextCertificatePEM,
- dump_certificate(FILETYPE_PEM, p12.get_ca_certificates()[0]))
-
-
- def gen_pkcs12(self, cert_pem=None, key_pem=None, ca_pem=None, friendly_name=None):
- """
- Generate a PKCS12 object with components from PEM. Verify that the set
- functions return None.
- """
- p12 = PKCS12()
- if cert_pem:
- ret = p12.set_certificate(load_certificate(FILETYPE_PEM, cert_pem))
- self.assertEqual(ret, None)
- if key_pem:
- ret = p12.set_privatekey(load_privatekey(FILETYPE_PEM, key_pem))
- self.assertEqual(ret, None)
- if ca_pem:
- ret = p12.set_ca_certificates((load_certificate(FILETYPE_PEM, ca_pem),))
- self.assertEqual(ret, None)
- if friendly_name:
- ret = p12.set_friendlyname(friendly_name)
- self.assertEqual(ret, None)
- return p12
-
-
- def check_recovery(self, p12_str, key=None, cert=None, ca=None, passwd='',
- extra=()):
- """
- Use openssl program to confirm three components are recoverable from a
- PKCS12 string.
- """
- if key:
- recovered_key = _runopenssl(
- p12_str, "pkcs12", '-nocerts', '-nodes', '-passin',
- '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)
- self.assertEqual(recovered_cert[-len(cert):], cert)
- if ca:
- recovered_cert = _runopenssl(
- p12_str, "pkcs12", '-cacerts', '-nodes', '-passin',
- 'pass:' + passwd, '-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.
- """
- passwd = 'whatever'
- pem = client_key_pem + client_cert_pem
- p12_str = _runopenssl(
- pem, "pkcs12", '-export', '-clcerts', '-passout', 'pass:' + passwd)
- p12 = load_pkcs12(p12_str, passwd)
- # verify
- self.assertTrue(isinstance(p12, PKCS12))
- cert_pem = dump_certificate(FILETYPE_PEM, p12.get_certificate())
- self.assertEqual(cert_pem, client_cert_pem)
- key_pem = dump_privatekey(FILETYPE_PEM, p12.get_privatekey())
- self.assertEqual(key_pem, client_key_pem)
- self.assertEqual(None, p12.get_ca_certificates())
-
-
- def test_load_pkcs12_garbage(self):
- """
- L{load_pkcs12} raises L{OpenSSL.crypto.Error} when passed a string
- which is not a PKCS12 dump.
- """
- passwd = 'whatever'
- e = self.assertRaises(Error, load_pkcs12, '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.
- """
- p12 = self.gen_pkcs12(client_cert_pem, client_key_pem, root_cert_pem)
- p12.set_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
- p12.set_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
- root_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
- client_cert = load_certificate(FILETYPE_PEM, client_cert_pem)
- p12.set_ca_certificates([root_cert]) # not a tuple
- self.assertEqual(1, len(p12.get_ca_certificates()))
- self.assertEqual(root_cert, p12.get_ca_certificates()[0])
- p12.set_ca_certificates([client_cert, root_cert])
- self.assertEqual(2, len(p12.get_ca_certificates()))
- self.assertEqual(client_cert, p12.get_ca_certificates()[0])
- self.assertEqual(root_cert, p12.get_ca_certificates()[1])
-
-
- 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}.
- """
- passwd = '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)
- self.assertEqual(p12.get_friendlyname(), friendly_name)
- dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3)
- reloaded_p12 = load_pkcs12(dumped_p12, passwd)
- self.assertEqual(
- p12.get_friendlyname(), reloaded_p12.get_friendlyname())
- # We would use the openssl program to confirm the friendly
- # name, but it is not possible. The pkcs12 command
- # does not store the friendly name in the cert's
- # alias, which we could then extract.
- self.check_recovery(
- dumped_p12, key=server_key_pem, cert=server_cert_pem,
- ca=root_cert_pem, passwd=passwd)
-
-
- def test_various_empty_passphrases(self):
- """
- Test that missing, None, and '' passphrases are identical for PKCS12
- export.
- """
- p12 = self.gen_pkcs12(client_cert_pem, client_key_pem, root_cert_pem)
- passwd = ''
- 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)
- for dumped_p12 in [dumped_p12_empty, dumped_p12_none, dumped_p12_nopw]:
- self.check_recovery(
- dumped_p12, key=client_key_pem, cert=client_cert_pem,
- ca=root_cert_pem, passwd=passwd)
-
-
- def test_removing_ca_cert(self):
- """
- Passing C{None} to L{PKCS12.set_ca_certificates} removes all CA
- certificates.
- """
- p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem)
- p12.set_ca_certificates(None)
- self.assertEqual(None, p12.get_ca_certificates())
-
-
- def test_export_without_mac(self):
- """
- Exporting a PKCS12 with a C{maciter} of C{-1} excludes the MAC
- entirely.
- """
- passwd = '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',))
-
-
- def test_load_without_mac(self):
- """
- Loading a PKCS12 without a MAC does something other than crash.
- """
- passwd = '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:
- recovered_p12 = load_pkcs12(dumped_p12, passwd)
- # The person who generated this PCKS12 should be flogged,
- # or better yet we should have a means to determine
- # whether a PCKS12 had a MAC that was verified.
- # Anyway, libopenssl chooses to allow it, so the
- # pyopenssl binding does as well.
- self.assertTrue(isinstance(recovered_p12, PKCS12))
- except Error:
- # Failing here with an exception is preferred as some openssl
- # versions do.
- pass
-
-
- def test_zero_len_list_for_ca(self):
- """
- A PKCS12 with an empty CA certificates list can be exported.
- """
- 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)
-
-
- def test_export_without_args(self):
- """
- All the arguments to L{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='')
-
-
- def test_key_cert_mismatch(self):
- """
- L{PKCS12.export} raises an exception when a key and certificate
- mismatch.
- """
- p12 = self.gen_pkcs12(server_cert_pem, client_key_pem, root_cert_pem)
- self.assertRaises(Error, p12.export)
-
-
-
-# These quoting functions taken directly from Twisted's twisted.python.win32.
-_cmdLineQuoteRe = re.compile(r'(\\*)"')
-_cmdLineQuoteRe2 = re.compile(r'(\\+)\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
-
- @rtype: C{str}
- @return: A cmd.exe-style quoted string
-
- @see: U{http://www.perlmonks.org/?node_id=764004}
- """
- s = _cmdLineQuoteRe2.sub(r"\1\1", _cmdLineQuoteRe.sub(r'\1\1\\"', s))
- return '"%s"' % s
-
-
-
-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.
-
- @type arguments: C{iterable} of C{str}
- @param arguments: An iterable of unquoted arguments to quote
-
- @rtype: C{str}
- @return: A space-delimited string containing quoted versions of L{arguments}
- """
- return ' '.join(map(cmdLineQuote, arguments))
-
-
-
-def _runopenssl(pem, *args):
- """
- Run the command line openssl tool with the given arguments and write
- the given PEM to its stdin. Not safe for quotes.
- """
- if os.name == 'posix':
- command = "openssl " + " ".join([
- "'%s'" % (arg.replace("'", "'\\''"),) for arg in args])
- else:
- command = "openssl " + quoteArguments(args)
- proc = Popen(command, shell=True, stdin=PIPE, stdout=PIPE)
- proc.stdin.write(pem)
- proc.stdin.close()
- return proc.stdout.read()
-
-
-
-class FunctionTests(TestCase):
- """
- Tests for free-functions in the L{OpenSSL.crypto} module.
- """
-
- def test_load_privatekey_invalid_format(self):
- """
- L{load_privatekey} raises L{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.
- """
- self.assertRaises(
- TypeError,
- load_privatekey,
- FILETYPE_PEM, encryptedPrivateKeyPEMPassphrase, object())
-
-
- def test_load_privatekey_wrong_args(self):
- """
- L{load_privatekey} raises L{TypeError} if called with the wrong number
- of arguments.
- """
- self.assertRaises(TypeError, load_privatekey)
-
-
- def test_load_privatekey_wrongPassphrase(self):
- """
- L{load_privatekey} raises L{OpenSSL.crypto.Error} when it is passed an
- encrypted PEM and an incorrect passphrase.
- """
- self.assertRaises(
- Error,
- load_privatekey, FILETYPE_PEM, encryptedPrivateKeyPEM, b("quack"))
-
-
- def test_load_privatekey_passphrase(self):
- """
- L{load_privatekey} can create a L{PKey} object from an encrypted PEM
- string if given the passphrase.
- """
- key = load_privatekey(
- FILETYPE_PEM, encryptedPrivateKeyPEM,
- encryptedPrivateKeyPEMPassphrase)
- self.assertTrue(isinstance(key, PKeyType))
-
-
- def test_load_privatekey_wrongPassphraseCallback(self):
- """
- L{load_privatekey} raises L{OpenSSL.crypto.Error} when it is passed an
- encrypted PEM and a passphrase callback which returns an incorrect
- passphrase.
- """
- called = []
- def cb(*a):
- called.append(None)
- return "quack"
- self.assertRaises(
- Error,
- load_privatekey, FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
- self.assertTrue(called)
-
-
- def test_load_privatekey_passphraseCallback(self):
- """
- L{load_privatekey} can create a L{PKey} object from an encrypted PEM
- string if given a passphrase callback which returns the correct
- password.
- """
- called = []
- def cb(writing):
- called.append(writing)
- return encryptedPrivateKeyPEMPassphrase
- key = load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
- self.assertTrue(isinstance(key, PKeyType))
- self.assertEqual(called, [False])
-
-
- def test_load_privatekey_passphrase_exception(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.
- """
- def broken(ignored):
- raise RuntimeError("This is not working.")
- self.assertRaises(
- Error,
- load_privatekey,
- FILETYPE_PEM, encryptedPrivateKeyPEM, broken)
-
-
- def test_dump_privatekey_wrong_args(self):
- """
- L{dump_privatekey} raises L{TypeError} if called with the wrong number
- of arguments.
- """
- self.assertRaises(TypeError, dump_privatekey)
-
-
- def test_dump_privatekey_unknown_cipher(self):
- """
- L{dump_privatekey} raises L{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")
-
-
- 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.
- """
- key = PKey()
- key.generate_key(TYPE_RSA, 512)
- self.assertRaises(
- TypeError,
- dump_privatekey, FILETYPE_PEM, key, "blowfish", object())
-
-
- def test_dump_privatekey_invalid_filetype(self):
- """
- L{dump_privatekey} raises L{ValueError} if called with an unrecognized
- filetype.
- """
- key = PKey()
- key.generate_key(TYPE_RSA, 512)
- self.assertRaises(ValueError, dump_privatekey, 100, key)
-
-
- def test_dump_privatekey_passphrase(self):
- """
- L{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))
- 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_certificate(self):
- """
- L{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")
- 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")
- self.assertEqual(dumped_text, good_text)
-
-
- def test_dump_privatekey(self):
- """
- L{dump_privatekey} writes a PEM, DER, and text.
- """
- key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
- self.assertTrue(key.check())
- dumped_pem = dump_privatekey(FILETYPE_PEM, key)
- self.assertEqual(dumped_pem, cleartextPrivateKeyPEM)
- 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")
- 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)
- dumped_text = dump_privatekey(FILETYPE_TEXT, key)
- good_text = _runopenssl(dumped_pem, "rsa", "-noout", "-text")
- self.assertEqual(dumped_text, good_text)
-
-
- def test_dump_certificate_request(self):
- """
- L{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")
- 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")
- 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
- returns the correct passphrase.
- """
- passphrase = b("foo")
- called = []
- def cb(writing):
- called.append(writing)
- return passphrase
- key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
- pem = dump_privatekey(FILETYPE_PEM, key, "blowfish", cb)
- self.assertTrue(isinstance(pem, bytes))
- self.assertEqual(called, [True])
- loadedKey = load_privatekey(FILETYPE_PEM, pem, passphrase)
- self.assertTrue(isinstance(loadedKey, PKeyType))
- self.assertEqual(loadedKey.type(), key.type())
- self.assertEqual(loadedKey.bits(), key.bits())
-
-
- def test_load_pkcs7_data(self):
- """
- L{load_pkcs7_data} accepts a PKCS#7 string and returns an instance of
- L{PKCS7Type}.
- """
- pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
- self.assertTrue(isinstance(pkcs7, PKCS7Type))
-
-
-
-class PKCS7Tests(TestCase):
- """
- Tests for L{PKCS7Type}.
- """
- def test_type(self):
- """
- L{PKCS7Type} is a type object.
- """
- self.assertTrue(isinstance(PKCS7Type, type))
- self.assertEqual(PKCS7Type.__name__, 'PKCS7')
-
- # XXX This doesn't currently work.
- # self.assertIdentical(PKCS7, PKCS7Type)
-
-
- # XXX Opposite results for all these following methods
-
- def test_type_is_signed_wrong_args(self):
- """
- L{PKCS7Type.type_is_signed} raises L{TypeError} if called with any
- arguments.
- """
- pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
- self.assertRaises(TypeError, pkcs7.type_is_signed, None)
-
-
- def test_type_is_signed(self):
- """
- L{PKCS7Type.type_is_signed} returns C{True} if the PKCS7 object is of
- the type I{signed}.
- """
- pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
- self.assertTrue(pkcs7.type_is_signed())
-
-
- def test_type_is_enveloped_wrong_args(self):
- """
- L{PKCS7Type.type_is_enveloped} raises L{TypeError} if called with any
- arguments.
- """
- pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
- self.assertRaises(TypeError, pkcs7.type_is_enveloped, None)
-
-
- 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}.
- """
- pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
- self.assertFalse(pkcs7.type_is_enveloped())
-
-
- def test_type_is_signedAndEnveloped_wrong_args(self):
- """
- L{PKCS7Type.type_is_signedAndEnveloped} raises L{TypeError} if called
- with any arguments.
- """
- pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
- self.assertRaises(TypeError, pkcs7.type_is_signedAndEnveloped, None)
-
-
- 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}.
- """
- pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
- self.assertFalse(pkcs7.type_is_signedAndEnveloped())
-
-
- def test_type_is_data(self):
- """
- L{PKCS7Type.type_is_data} returns C{False} if the PKCS7 object is not of
- the type data.
- """
- pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
- self.assertFalse(pkcs7.type_is_data())
-
-
- def test_type_is_data_wrong_args(self):
- """
- L{PKCS7Type.type_is_data} raises L{TypeError} if called with any
- arguments.
- """
- pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
- self.assertRaises(TypeError, pkcs7.type_is_data, None)
-
-
- def test_get_type_name_wrong_args(self):
- """
- L{PKCS7Type.get_type_name} raises L{TypeError} if called with any
- arguments.
- """
- pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
- self.assertRaises(TypeError, pkcs7.get_type_name, None)
-
-
- def test_get_type_name(self):
- """
- L{PKCS7Type.get_type_name} returns a C{str} giving the type name.
- """
- pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
- self.assertEquals(pkcs7.get_type_name(), b('pkcs7-signedData'))
-
-
- 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.
- """
- pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
- self.assertRaises(AttributeError, getattr, pkcs7, "foo")
-
-
-
-class NetscapeSPKITests(TestCase, _PKeyInteractionTestsMixin):
- """
- Tests for L{OpenSSL.crypto.NetscapeSPKI}.
- """
- def signable(self):
- """
- Return a new L{NetscapeSPKI} for use with signing tests.
- """
- return NetscapeSPKI()
-
-
- def test_type(self):
- """
- L{NetscapeSPKI} and L{NetscapeSPKIType} refer to the same type object
- and can be used to create instances of that type.
- """
- self.assertIdentical(NetscapeSPKI, NetscapeSPKIType)
- self.assertConsistentType(NetscapeSPKI, 'NetscapeSPKI')
-
-
- def test_construction(self):
- """
- L{NetscapeSPKI} returns an instance of L{NetscapeSPKIType}.
- """
- nspki = NetscapeSPKI()
- self.assertTrue(isinstance(nspki, NetscapeSPKIType))
-
-
- def test_invalid_attribute(self):
- """
- Accessing a non-existent attribute of a L{NetscapeSPKI} instance causes
- an L{AttributeError} to be raised.
- """
- nspki = NetscapeSPKI()
- self.assertRaises(AttributeError, lambda: nspki.foo)
-
-
- def test_b64_encode(self):
- """
- L{NetscapeSPKI.b64_encode} encodes the certificate to a base64 blob.
- """
- nspki = NetscapeSPKI()
- blob = nspki.b64_encode()
- self.assertTrue(isinstance(blob, bytes))
-
-
-
-class RevokedTests(TestCase):
- """
- Tests for L{OpenSSL.crypto.Revoked}
- """
- def test_construction(self):
- """
- Confirm we can create L{OpenSSL.crypto.Revoked}. Check
- that it is empty.
- """
- revoked = Revoked()
- self.assertTrue(isinstance(revoked, Revoked))
- self.assertEquals(type(revoked), Revoked)
- self.assertEquals(revoked.get_serial(), b('00'))
- self.assertEquals(revoked.get_rev_date(), None)
- self.assertEquals(revoked.get_reason(), None)
-
-
- def test_construction_wrong_args(self):
- """
- Calling L{OpenSSL.crypto.Revoked} with any arguments results
- in a L{TypeError} being raised.
- """
- self.assertRaises(TypeError, Revoked, None)
- self.assertRaises(TypeError, Revoked, 1)
- self.assertRaises(TypeError, Revoked, "foo")
-
-
- def test_serial(self):
- """
- Confirm we can set and get serial numbers from
- L{OpenSSL.crypto.Revoked}. Confirm errors are handled
- with grace.
- """
- revoked = Revoked()
- ret = revoked.set_serial(b('10b'))
- self.assertEquals(ret, None)
- ser = revoked.get_serial()
- self.assertEquals(ser, b('010B'))
-
- revoked.set_serial(b('31ppp')) # a type error would be nice
- ser = revoked.get_serial()
- self.assertEquals(ser, b('31'))
-
- self.assertRaises(ValueError, revoked.set_serial, b('pqrst'))
- self.assertRaises(TypeError, revoked.set_serial, 100)
- self.assertRaises(TypeError, revoked.get_serial, 1)
- self.assertRaises(TypeError, revoked.get_serial, None)
- self.assertRaises(TypeError, revoked.get_serial, "")
-
-
- def test_date(self):
- """
- Confirm we can set and get revocation dates from
- L{OpenSSL.crypto.Revoked}. Confirm errors are handled
- with grace.
- """
- revoked = Revoked()
- date = revoked.get_rev_date()
- self.assertEquals(date, None)
-
- now = b(datetime.now().strftime("%Y%m%d%H%M%SZ"))
- ret = revoked.set_rev_date(now)
- self.assertEqual(ret, None)
- date = revoked.get_rev_date()
- self.assertEqual(date, now)
-
-
- def test_reason(self):
- """
- Confirm we can set and get revocation reasons from
- L{OpenSSL.crypto.Revoked}. The "get" need to work
- as "set". Likewise, each reason of all_reasons() must work.
- """
- revoked = Revoked()
- for r in revoked.all_reasons():
- for x in range(2):
- ret = revoked.set_reason(r)
- self.assertEquals(ret, None)
- reason = revoked.get_reason()
- self.assertEquals(
- reason.lower().replace(b(' '), b('')),
- r.lower().replace(b(' '), b('')))
- r = reason # again with the resp of get
-
- revoked.set_reason(None)
- self.assertEqual(revoked.get_reason(), None)
-
-
- def test_set_reason_wrong_arguments(self):
- """
- Calling L{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.
- """
- revoked = Revoked()
- self.assertRaises(TypeError, revoked.set_reason, 100)
- self.assertRaises(ValueError, revoked.set_reason, b('blue'))
-
-
- def test_get_reason_wrong_arguments(self):
- """
- Calling L{OpenSSL.crypto.Revoked.get_reason} with any
- arguments results in L{TypeError} being raised.
- """
- revoked = Revoked()
- self.assertRaises(TypeError, revoked.get_reason, None)
- self.assertRaises(TypeError, revoked.get_reason, 1)
- self.assertRaises(TypeError, revoked.get_reason, "foo")
-
-
-
-class CRLTests(TestCase):
- """
- Tests for L{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
- that it is empty
- """
- crl = CRL()
- self.assertTrue( isinstance(crl, CRL) )
- self.assertEqual(crl.get_revoked(), None)
-
-
- def test_construction_wrong_args(self):
- """
- Calling L{OpenSSL.crypto.CRL} with any number of arguments
- results in a L{TypeError} being raised.
- """
- self.assertRaises(TypeError, CRL, 1)
- self.assertRaises(TypeError, CRL, "")
- self.assertRaises(TypeError, CRL, None)
-
-
- def test_export(self):
- """
- Use python to create a simple CRL with a revocation, and export
- the CRL in formats of PEM, DER and text. Those outputs are verified
- with the openssl program.
- """
- crl = CRL()
- revoked = Revoked()
- now = b(datetime.now().strftime("%Y%m%d%H%M%SZ"))
- revoked.set_rev_date(now)
- revoked.set_serial(b('3ab'))
- revoked.set_reason(b('sUpErSeDEd'))
- crl.add_revoked(revoked)
-
- # PEM format
- dumped_crl = crl.export(self.cert, self.pkey, days=20)
- text = _runopenssl(dumped_crl, "crl", "-noout", "-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.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'))
-
- # text format
- dumped_text = crl.export(self.cert, self.pkey, type=FILETYPE_TEXT)
- self.assertEqual(text, dumped_text)
-
-
- def test_add_revoked_keyword(self):
- """
- L{OpenSSL.CRL.add_revoked} accepts its single argument as the
- I{revoked} keyword argument.
- """
- crl = CRL()
- revoked = Revoked()
- crl.add_revoked(revoked=revoked)
- self.assertTrue(isinstance(crl.get_revoked()[0], Revoked))
-
-
- def test_export_wrong_args(self):
- """
- Calling L{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.
- """
- crl = CRL()
- self.assertRaises(TypeError, crl.export)
- self.assertRaises(TypeError, crl.export, self.cert)
- self.assertRaises(TypeError, crl.export, self.cert, self.pkey, FILETYPE_PEM, 10, "foo")
-
- self.assertRaises(TypeError, crl.export, None, self.pkey, FILETYPE_PEM, 10)
- self.assertRaises(TypeError, crl.export, self.cert, None, FILETYPE_PEM, 10)
- self.assertRaises(TypeError, crl.export, self.cert, self.pkey, None, 10)
- self.assertRaises(TypeError, crl.export, self.cert, FILETYPE_PEM, None)
-
-
- 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.
- """
- crl = CRL()
- self.assertRaises(ValueError, crl.export, self.cert, self.pkey, 100, 10)
-
-
- 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
- verify them.
- """
- crl = CRL()
-
- revoked = Revoked()
- now = b(datetime.now().strftime("%Y%m%d%H%M%SZ"))
- revoked.set_rev_date(now)
- revoked.set_serial(b('3ab'))
- crl.add_revoked(revoked)
- revoked.set_serial(b('100'))
- revoked.set_reason(b('sUpErSeDEd'))
- crl.add_revoked(revoked)
-
- revs = crl.get_revoked()
- self.assertEqual(len(revs), 2)
- self.assertEqual(type(revs[0]), Revoked)
- self.assertEqual(type(revs[1]), Revoked)
- self.assertEqual(revs[0].get_serial(), b('03AB'))
- self.assertEqual(revs[1].get_serial(), b('0100'))
- self.assertEqual(revs[0].get_rev_date(), now)
- self.assertEqual(revs[1].get_rev_date(), now)
-
-
- def test_get_revoked_wrong_args(self):
- """
- Calling L{OpenSSL.CRL.get_revoked} with any arguments results
- in a L{TypeError} being raised.
- """
- crl = CRL()
- self.assertRaises(TypeError, crl.get_revoked, None)
- self.assertRaises(TypeError, crl.get_revoked, 1)
- self.assertRaises(TypeError, crl.get_revoked, "")
- self.assertRaises(TypeError, crl.get_revoked, "", 1, None)
-
-
- 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.
- """
- crl = CRL()
- self.assertRaises(TypeError, crl.add_revoked)
- self.assertRaises(TypeError, crl.add_revoked, 1, 2)
- self.assertRaises(TypeError, crl.add_revoked, "foo", "bar")
-
-
- def test_load_crl(self):
- """
- Load a known CRL and inspect its revocations. Both
- PEM and DER formats are loaded.
- """
- crl = load_crl(FILETYPE_PEM, crlData)
- revs = crl.get_revoked()
- self.assertEqual(len(revs), 2)
- self.assertEqual(revs[0].get_serial(), b('03AB'))
- self.assertEqual(revs[0].get_reason(), None)
- self.assertEqual(revs[1].get_serial(), b('0100'))
- self.assertEqual(revs[1].get_reason(), b('Superseded'))
-
- der = _runopenssl(crlData, "crl", "-outform", "DER")
- crl = load_crl(FILETYPE_ASN1, der)
- revs = crl.get_revoked()
- self.assertEqual(len(revs), 2)
- self.assertEqual(revs[0].get_serial(), b('03AB'))
- self.assertEqual(revs[0].get_reason(), None)
- self.assertEqual(revs[1].get_serial(), b('0100'))
- self.assertEqual(revs[1].get_reason(), b('Superseded'))
-
-
- 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.
- """
- self.assertRaises(TypeError, load_crl)
- self.assertRaises(TypeError, load_crl, FILETYPE_PEM)
- self.assertRaises(TypeError, load_crl, FILETYPE_PEM, crlData, None)
-
-
- def test_load_crl_bad_filetype(self):
- """
- Calling L{OpenSSL.crypto.load_crl} with an unknown file type
- raises a L{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}.
- """
- self.assertRaises(Error, load_crl, FILETYPE_PEM, "hello, world")
-
-
-class SignVerifyTests(TestCase):
- """
- Tests for L{OpenSSL.crypto.sign} and L{OpenSSL.crypto.verify}.
- """
- def test_sign_verify(self):
- """
- L{sign} generates a cryptographic signature which L{verify} can check.
- """
- content = b(
- "It was a bright cold day in April, and the clocks were striking "
- "thirteen. Winston Smith, his chin nuzzled into his breast in an "
- "effort to escape the vile wind, slipped quickly through the "
- "glass doors of Victory Mansions, though not quickly enough to "
- "prevent a swirl of gritty dust from entering along with him.")
-
- # sign the content with this private key
- priv_key = load_privatekey(FILETYPE_PEM, root_key_pem)
- # verify the content with this cert
- good_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
- # certificate unrelated to priv_key, used to trigger an error
- bad_cert = load_certificate(FILETYPE_PEM, server_cert_pem)
-
- for digest in ['md5', 'sha1']:
- sig = sign(priv_key, content, digest)
-
- # Verify the signature of content, will throw an exception if error.
- verify(good_cert, sig, content, digest)
-
- # This should fail because the certificate doesn't match the
- # private key that was used to sign the content.
- self.assertRaises(Error, verify, bad_cert, sig, content, digest)
-
- # This should fail because we've "tainted" the content after
- # signing it.
- self.assertRaises(
- Error, verify,
- good_cert, sig, content + b("tainted"), digest)
-
- # test that unknown digest types fail
- self.assertRaises(
- ValueError, sign, priv_key, content, "strange-digest")
- self.assertRaises(
- ValueError, verify, good_cert, sig, content, "strange-digest")
-
-
- def test_sign_nulls(self):
- """
- L{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)
- good_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
- sig = sign(priv_key, content, "sha1")
- verify(good_cert, sig, content, "sha1")
-
-
-if __name__ == '__main__':
- main()
+++ /dev/null
-# Copyright (c) Frederick Dean
-# See LICENSE for details.
-
-"""
-Unit tests for L{OpenSSL.rand}.
-"""
-
-from unittest import main
-import os
-import stat
-
-from OpenSSL.test.util import TestCase, b
-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.
- """
- 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_bytes(self):
- """
- Verify that we can obtain bytes from rand_bytes() and
- that they are different each time. Test the parameter
- of rand_bytes() for bad values.
- """
- b1 = rand.bytes(50)
- self.assertEqual(len(b1), 50)
- b2 = rand.bytes(num_bytes=50) # parameter by name
- self.assertNotEqual(b1, b2) # Hip, Hip, Horay! FIPS complaince
- b3 = rand.bytes(num_bytes=0)
- self.assertEqual(len(b3), 0)
- exc = self.assertRaises(ValueError, rand.bytes, -1)
- self.assertEqual(str(exc), "num_bytes must not be negative")
-
-
- 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}.
- """
- self.assertRaises(TypeError, rand.add)
- self.assertRaises(TypeError, rand.add, b("foo"), None)
- self.assertRaises(TypeError, rand.add, None, 3)
- self.assertRaises(TypeError, rand.add, b("foo"), 3, None)
-
-
- def test_add(self):
- """
- L{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}.
- """
- self.assertRaises(TypeError, rand.seed)
- self.assertRaises(TypeError, rand.seed, None)
- self.assertRaises(TypeError, rand.seed, b("foo"), None)
-
-
- def test_seed(self):
- """
- L{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
- arguments.
- """
- self.assertRaises(TypeError, rand.status, None)
-
-
- def test_status(self):
- """
- L{OpenSSL.rand.status} returns C{True} if the PRNG has sufficient
- entropy, C{False} otherwise.
- """
- # It's hard to know what it is actually going to return. Different
- # OpenSSL random engines decide differently whether they have enough
- # entropy or not.
- self.assertTrue(rand.status() in (1, 2))
-
-
- 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}.
- """
- self.assertRaises(TypeError, rand.egd)
- self.assertRaises(TypeError, rand.egd, None)
- self.assertRaises(TypeError, rand.egd, "foo", None)
- self.assertRaises(TypeError, rand.egd, None, 3)
- self.assertRaises(TypeError, rand.egd, "foo", 3, None)
-
-
- 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.
- """
- result = rand.egd(self.mktemp())
- 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
- arguments.
- """
- self.assertRaises(TypeError, rand.cleanup, None)
-
-
- def test_cleanup(self):
- """
- L{OpenSSL.rand.cleanup} releases the memory used by the PRNG and returns
- C{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}.
- """
- self.assertRaises(TypeError, rand.load_file)
- self.assertRaises(TypeError, rand.load_file, "foo", None)
- self.assertRaises(TypeError, rand.load_file, None, 1)
- self.assertRaises(TypeError, rand.load_file, "foo", 1, None)
-
-
- 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.
- """
- self.assertRaises(TypeError, rand.write_file)
- self.assertRaises(TypeError, rand.write_file, None)
- self.assertRaises(TypeError, rand.write_file, "foo", None)
-
-
- def test_files(self):
- """
- Test reading and writing of files via rand functions.
- """
- # Write random bytes to a file
- tmpfile = self.mktemp()
- # Make sure it exists (so cleanup definitely succeeds)
- fObj = open(tmpfile, 'w')
- fObj.close()
- try:
- rand.write_file(tmpfile)
- # Verify length of written file
- size = os.stat(tmpfile)[stat.ST_SIZE]
- self.assertEquals(size, 1024)
- # Read random bytes from file
- rand.load_file(tmpfile)
- rand.load_file(tmpfile, 4) # specify a length
- finally:
- # Cleanup
- os.unlink(tmpfile)
-
-
-if __name__ == '__main__':
- main()
+++ /dev/null
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-
-"""
-Unit tests for L{OpenSSL.SSL}.
-"""
-
-from gc import collect
-from errno import ECONNREFUSED, EINPROGRESS, EWOULDBLOCK
-from sys import platform, version_info
-from socket import error, socket
-from os import makedirs
-from os.path import join
-from unittest import main
-from weakref import ref
-
-from OpenSSL.crypto import TYPE_RSA, FILETYPE_PEM
-from OpenSSL.crypto import PKey, X509, X509Extension
-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 (
- VERIFY_PEER, VERIFY_FAIL_IF_NO_PEER_CERT, VERIFY_CLIENT_ONCE, VERIFY_NONE)
-from OpenSSL.SSL import (
- Error, SysCallError, WantReadError, ZeroReturnError, SSLeay_version)
-from OpenSSL.SSL import Context, ContextType, Connection, ConnectionType
-
-from OpenSSL.test.util import TestCase, bytes, b
-from OpenSSL.test.test_crypto import (
- cleartextCertificatePEM, cleartextPrivateKeyPEM)
-from OpenSSL.test.test_crypto import (
- client_cert_pem, client_key_pem, server_cert_pem, server_key_pem,
- root_cert_pem)
-
-try:
- from OpenSSL.SSL import OP_NO_QUERY_MTU
-except ImportError:
- OP_NO_QUERY_MTU = None
-try:
- from OpenSSL.SSL import OP_COOKIE_EXCHANGE
-except ImportError:
- OP_COOKIE_EXCHANGE = None
-try:
- from OpenSSL.SSL import OP_NO_TICKET
-except ImportError:
- OP_NO_TICKET = 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,
- SSL_CB_LOOP, SSL_CB_EXIT, SSL_CB_READ, SSL_CB_WRITE, SSL_CB_ALERT,
- SSL_CB_READ_ALERT, SSL_CB_WRITE_ALERT, SSL_CB_ACCEPT_LOOP,
- SSL_CB_ACCEPT_EXIT, SSL_CB_CONNECT_LOOP, SSL_CB_CONNECT_EXIT,
- SSL_CB_HANDSHAKE_START, SSL_CB_HANDSHAKE_DONE)
-
-# openssl dhparam 128 -out dh-128.pem (note that 128 is a small number of bits
-# to use)
-dhparam = """\
------BEGIN DH PARAMETERS-----
-MBYCEQCobsg29c9WZP/54oAPcwiDAgEC
------END DH PARAMETERS-----
-"""
-
-
-def verify_cb(conn, cert, errnum, depth, ok):
- return ok
-
-
-def socket_pair():
- """
- Establish and return a pair of network sockets connected to each other.
- """
- # Connect a pair of sockets
- port = socket()
- port.bind(('', 0))
- port.listen(1)
- client = socket()
- client.setblocking(False)
- client.connect_ex(("127.0.0.1", port.getsockname()[1]))
- client.setblocking(True)
- server = port.accept()[0]
-
- # Let's pass some unencrypted data to make sure our socket connection is
- # fine. Just one byte, so we don't have to worry about buffers getting
- # filled up or fragmentation.
- server.send(b("x"))
- assert client.recv(1024) == b("x")
- client.send(b("y"))
- assert server.recv(1024) == b("y")
-
- # Most of our callers want non-blocking sockets, make it easy for them.
- server.setblocking(False)
- client.setblocking(False)
-
- return (server, client)
-
-
-
-def handshake(client, server):
- conns = [client, server]
- while conns:
- for conn in conns:
- try:
- conn.do_handshake()
- except WantReadError:
- pass
- else:
- conns.remove(conn)
-
-
-def _create_certificate_chain():
- """
- Construct and return a chain of certificates.
-
- 1. A new self-signed certificate authority certificate (cacert)
- 2. A new intermediate certificate signed by cacert (icert)
- 3. A new server certificate signed by icert (scert)
- """
- caext = X509Extension(b('basicConstraints'), False, b('CA:true'))
-
- # Step 1
- cakey = PKey()
- cakey.generate_key(TYPE_RSA, 512)
- cacert = X509()
- cacert.get_subject().commonName = "Authority Certificate"
- cacert.set_issuer(cacert.get_subject())
- cacert.set_pubkey(cakey)
- cacert.set_notBefore(b("20000101000000Z"))
- cacert.set_notAfter(b("20200101000000Z"))
- cacert.add_extensions([caext])
- cacert.set_serial_number(0)
- cacert.sign(cakey, "sha1")
-
- # Step 2
- ikey = PKey()
- ikey.generate_key(TYPE_RSA, 512)
- icert = X509()
- icert.get_subject().commonName = "Intermediate Certificate"
- icert.set_issuer(cacert.get_subject())
- icert.set_pubkey(ikey)
- icert.set_notBefore(b("20000101000000Z"))
- icert.set_notAfter(b("20200101000000Z"))
- icert.add_extensions([caext])
- icert.set_serial_number(0)
- icert.sign(cakey, "sha1")
-
- # Step 3
- skey = PKey()
- skey.generate_key(TYPE_RSA, 512)
- scert = X509()
- scert.get_subject().commonName = "Server Certificate"
- scert.set_issuer(icert.get_subject())
- scert.set_pubkey(skey)
- scert.set_notBefore(b("20000101000000Z"))
- scert.set_notAfter(b("20200101000000Z"))
- scert.add_extensions([
- X509Extension(b('basicConstraints'), True, b('CA:false'))])
- scert.set_serial_number(0)
- scert.sign(ikey, "sha1")
-
- return [(cakey, cacert), (ikey, icert), (skey, scert)]
-
-
-
-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()
-
- 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.set_accept_state()
- client = Connection(Context(TLSv1_METHOD), client)
- client.set_connect_state()
-
- handshake(client, server)
-
- server.setblocking(True)
- client.setblocking(True)
- return server, client
-
-
- def _interactInMemory(self, client_conn, server_conn):
- """
- Try to read application bytes from each of the two L{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
- some application bytes, return a two-tuple of the connection from which
- the bytes were read and the bytes themselves.
- """
- wrote = True
- while wrote:
- # Loop until neither side has anything to say
- wrote = False
-
- # Copy stuff from each side's send buffer to the other side's
- # receive buffer.
- for (read, write) in [(client_conn, server_conn),
- (server_conn, client_conn)]:
-
- # Give the side a chance to generate some more bytes, or
- # succeed.
- try:
- data = read.recv(2 ** 16)
- except WantReadError:
- # It didn't succeed, so we'll hope it generated some
- # output.
- pass
- else:
- # It did succeed, so we'll stop now and let the caller deal
- # with it.
- return (read, data)
-
- while True:
- # Keep copying as long as there's more stuff there.
- try:
- dirty = read.bio_read(4096)
- except WantReadError:
- # Okay, nothing more waiting to be sent. Stop
- # processing this send buffer.
- break
- else:
- # Keep track of the fact that someone generated some
- # output.
- wrote = True
- write.bio_write(dirty)
-
-
-
-class VersionTests(TestCase):
- """
- Tests for version information exposed by
- L{OpenSSL.SSL.SSLeay_version} and
- L{OpenSSL.SSL.OPENSSL_VERSION_NUMBER}.
- """
- def test_OPENSSL_VERSION_NUMBER(self):
- """
- L{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.
- """
- self.assertTrue(isinstance(OPENSSL_VERSION_NUMBER, int))
-
-
- def test_SSLeay_version(self):
- """
- L{SSLeay_version} takes a version type indicator and returns
- one of a number of version strings based on that indicator.
- """
- versions = {}
- for t in [SSLEAY_VERSION, SSLEAY_CFLAGS, SSLEAY_BUILT_ON,
- SSLEAY_PLATFORM, SSLEAY_DIR]:
- version = SSLeay_version(t)
- versions[version] = t
- self.assertTrue(isinstance(version, bytes))
- self.assertEqual(len(versions), 5)
-
-
-
-class ContextTests(TestCase, _LoopbackMixin):
- """
- Unit tests for L{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}.
- """
- for meth in [SSLv3_METHOD, SSLv23_METHOD, TLSv1_METHOD]:
- Context(meth)
-
- try:
- Context(SSLv2_METHOD)
- except ValueError:
- # Some versions of OpenSSL have SSLv2, some don't.
- # Difficult to say in advance.
- pass
-
- self.assertRaises(TypeError, Context, "")
- self.assertRaises(ValueError, Context, 10)
-
-
- def test_type(self):
- """
- L{Context} and L{ContextType} refer to the same type object and can be
- used to create instances of that type.
- """
- self.assertIdentical(Context, ContextType)
- self.assertConsistentType(Context, 'Context', TLSv1_METHOD)
-
-
- def test_use_privatekey(self):
- """
- L{Context.use_privatekey} takes an L{OpenSSL.crypto.PKey} instance.
- """
- key = PKey()
- key.generate_key(TYPE_RSA, 128)
- ctx = Context(TLSv1_METHOD)
- ctx.use_privatekey(key)
- self.assertRaises(TypeError, ctx.use_privatekey, "")
-
-
- def test_set_app_data_wrong_args(self):
- """
- L{Context.set_app_data} raises L{TypeError} if called with other than
- one argument.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.set_app_data)
- self.assertRaises(TypeError, context.set_app_data, None, None)
-
-
- def test_get_app_data_wrong_args(self):
- """
- L{Context.get_app_data} raises L{TypeError} if called with any
- arguments.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.get_app_data, None)
-
-
- def test_app_data(self):
- """
- L{Context.set_app_data} stores an object for later retrieval using
- L{Context.get_app_data}.
- """
- app_data = object()
- context = Context(TLSv1_METHOD)
- context.set_app_data(app_data)
- self.assertIdentical(context.get_app_data(), app_data)
-
-
- 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.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.set_options)
- self.assertRaises(TypeError, context.set_options, None)
- self.assertRaises(TypeError, context.set_options, 1, None)
-
-
- 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.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.set_timeout)
- self.assertRaises(TypeError, context.set_timeout, None)
- self.assertRaises(TypeError, context.set_timeout, 1, None)
-
-
- def test_get_timeout_wrong_args(self):
- """
- L{Context.get_timeout} raises L{TypeError} if called with any arguments.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.get_timeout, None)
-
-
- 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
- value.
- """
- context = Context(TLSv1_METHOD)
- context.set_timeout(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.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.set_verify_depth)
- self.assertRaises(TypeError, context.set_verify_depth, None)
- self.assertRaises(TypeError, context.set_verify_depth, 1, None)
-
-
- def test_get_verify_depth_wrong_args(self):
- """
- L{Context.get_verify_depth} raises L{TypeError} if called with any arguments.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.get_verify_depth, None)
-
-
- def test_verify_depth(self):
- """
- L{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}.
- """
- context = Context(TLSv1_METHOD)
- context.set_verify_depth(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
- passphrase. Return the path to the new file.
- """
- key = PKey()
- key.generate_key(TYPE_RSA, 128)
- pemFile = self.mktemp()
- fObj = open(pemFile, 'w')
- pem = dump_privatekey(FILETYPE_PEM, key, "blowfish", passphrase)
- fObj.write(pem.decode('ascii'))
- fObj.close()
- return pemFile
-
-
- def test_set_passwd_cb_wrong_args(self):
- """
- L{Context.set_passwd_cb} raises L{TypeError} if called with the
- wrong arguments or with a non-callable first argument.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.set_passwd_cb)
- self.assertRaises(TypeError, context.set_passwd_cb, None)
- self.assertRaises(TypeError, context.set_passwd_cb, lambda: None, None, None)
-
-
- def test_set_passwd_cb(self):
- """
- L{Context.set_passwd_cb} accepts a callable which will be invoked when
- a private key is loaded from an encrypted PEM.
- """
- passphrase = b("foobar")
- pemFile = self._write_encrypted_pem(passphrase)
- calledWith = []
- def passphraseCallback(maxlen, verify, extra):
- calledWith.append((maxlen, verify, extra))
- return passphrase
- context = Context(TLSv1_METHOD)
- context.set_passwd_cb(passphraseCallback)
- context.use_privatekey_file(pemFile)
- self.assertTrue(len(calledWith), 1)
- self.assertTrue(isinstance(calledWith[0][0], int))
- self.assertTrue(isinstance(calledWith[0][1], int))
- self.assertEqual(calledWith[0][2], None)
-
-
- def test_passwd_callback_exception(self):
- """
- L{Context.use_privatekey_file} propagates any exception raised by the
- passphrase callback.
- """
- pemFile = self._write_encrypted_pem(b("monkeys are nice"))
- def passphraseCallback(maxlen, verify, extra):
- raise RuntimeError("Sorry, I am a fail.")
-
- context = Context(TLSv1_METHOD)
- context.set_passwd_cb(passphraseCallback)
- self.assertRaises(RuntimeError, context.use_privatekey_file, pemFile)
-
-
- def test_passwd_callback_false(self):
- """
- L{Context.use_privatekey_file} raises L{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
-
- context = Context(TLSv1_METHOD)
- context.set_passwd_cb(passphraseCallback)
- self.assertRaises(Error, context.use_privatekey_file, pemFile)
-
-
- def test_passwd_callback_non_string(self):
- """
- L{Context.use_privatekey_file} raises L{OpenSSL.SSL.Error} if the
- passphrase callback returns a true non-string value.
- """
- pemFile = self._write_encrypted_pem(b("monkeys are nice"))
- def passphraseCallback(maxlen, verify, extra):
- return 10
-
- context = Context(TLSv1_METHOD)
- context.set_passwd_cb(passphraseCallback)
- self.assertRaises(Error, context.use_privatekey_file, pemFile)
-
-
- def test_passwd_callback_too_long(self):
- """
- If the passphrase returned by the passphrase callback returns a string
- longer than the indicated maximum length, it is truncated.
- """
- # A priori knowledge!
- passphrase = b("x") * 1024
- pemFile = self._write_encrypted_pem(passphrase)
- def passphraseCallback(maxlen, verify, extra):
- assert maxlen == 1024
- return passphrase + b("y")
-
- context = Context(TLSv1_METHOD)
- context.set_passwd_cb(passphraseCallback)
- # This shall succeed because the truncated result is the correct
- # passphrase.
- context.use_privatekey_file(pemFile)
-
-
- def test_set_info_callback(self):
- """
- L{Context.set_info_callback} accepts a callable which will be invoked
- when certain information about an SSL connection is available.
- """
- (server, client) = socket_pair()
-
- clientSSL = Connection(Context(TLSv1_METHOD), client)
- clientSSL.set_connect_state()
-
- called = []
- def info(conn, where, ret):
- called.append((conn, where, ret))
- context = Context(TLSv1_METHOD)
- context.set_info_callback(info)
- context.use_certificate(
- load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
- context.use_privatekey(
- load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
-
- serverSSL = Connection(context, server)
- serverSSL.set_accept_state()
-
- while not called:
- for ssl in clientSSL, serverSSL:
- try:
- ssl.do_handshake()
- except WantReadError:
- pass
-
- # Kind of lame. Just make sure it got called somehow.
- self.assertTrue(called)
-
-
- 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.
- """
- (server, client) = socket_pair()
-
- clientContext = Context(TLSv1_METHOD)
- clientContext.load_verify_locations(*args)
- # Require that the server certificate verify properly or the
- # connection will fail.
- clientContext.set_verify(
- VERIFY_PEER,
- lambda conn, cert, errno, depth, preverify_ok: preverify_ok)
-
- clientSSL = Connection(clientContext, client)
- clientSSL.set_connect_state()
-
- serverContext = Context(TLSv1_METHOD)
- serverContext.use_certificate(
- load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
- serverContext.use_privatekey(
- load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
-
- serverSSL = Connection(serverContext, server)
- serverSSL.set_accept_state()
-
- # Without load_verify_locations above, the handshake
- # will fail:
- # Error: [('SSL routines', 'SSL3_GET_SERVER_CERTIFICATE',
- # 'certificate verify failed')]
- handshake(clientSSL, serverSSL)
-
- cert = clientSSL.get_peer_certificate()
- self.assertEqual(cert.get_subject().CN, 'Testing Root CA')
-
-
- def test_load_verify_file(self):
- """
- L{Context.load_verify_locations} accepts a file name and uses the
- certificates within for verification purposes.
- """
- cafile = self.mktemp()
- fObj = open(cafile, 'w')
- fObj.write(cleartextCertificatePEM.decode('ascii'))
- fObj.close()
-
- self._load_verify_locations_test(cafile)
-
-
- def test_load_verify_invalid_file(self):
- """
- L{Context.load_verify_locations} raises L{Error} when passed a
- non-existent cafile.
- """
- clientContext = Context(TLSv1_METHOD)
- self.assertRaises(
- Error, clientContext.load_verify_locations, self.mktemp())
-
-
- def test_load_verify_directory(self):
- """
- L{Context.load_verify_locations} accepts a directory name and uses
- the certificates within for verification purposes.
- """
- capath = self.mktemp()
- makedirs(capath)
- # 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']:
- cafile = join(capath, name)
- fObj = open(cafile, 'w')
- fObj.write(cleartextCertificatePEM.decode('ascii'))
- fObj.close()
-
- self._load_verify_locations_test(None, capath)
-
-
- 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.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.load_verify_locations)
- self.assertRaises(TypeError, context.load_verify_locations, object())
- self.assertRaises(TypeError, context.load_verify_locations, object(), object())
- self.assertRaises(TypeError, context.load_verify_locations, None, None, None)
-
-
- if platform == "win32":
- "set_default_verify_paths appears not to work on Windows. "
- "See LP#404343 and LP#404344."
- else:
- def test_set_default_verify_paths(self):
- """
- L{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
- # the CAs in the platform CA location. Getting one of those costs
- # money. Fortunately (or unfortunately, depending on your
- # perspective), it's easy to think of a public server on the
- # internet which has such a certificate. Connecting to the network
- # in a unit test is bad, but it's the only way I can think of to
- # really test this. -exarkun
-
- # Arg, verisign.com doesn't speak TLSv1
- context = Context(SSLv3_METHOD)
- context.set_default_verify_paths()
- context.set_verify(
- VERIFY_PEER,
- lambda conn, cert, errno, depth, preverify_ok: preverify_ok)
-
- client = socket()
- client.connect(('verisign.com', 443))
- clientSSL = Connection(context, client)
- clientSSL.set_connect_state()
- clientSSL.do_handshake()
- clientSSL.send('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.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.set_default_verify_paths, None)
- self.assertRaises(TypeError, context.set_default_verify_paths, 1)
- self.assertRaises(TypeError, context.set_default_verify_paths, "")
-
-
- def test_add_extra_chain_cert_invalid_cert(self):
- """
- L{Context.add_extra_chain_cert} raises L{TypeError} if called with
- other than one argument or if called with an object which is not an
- instance of L{X509}.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.add_extra_chain_cert)
- self.assertRaises(TypeError, context.add_extra_chain_cert, object())
- self.assertRaises(TypeError, context.add_extra_chain_cert, object(), object())
-
-
- def _handshake_test(self, serverContext, clientContext):
- """
- Verify that a client and server created with the given contexts can
- successfully handshake and communicate.
- """
- serverSocket, clientSocket = socket_pair()
-
- server = Connection(serverContext, serverSocket)
- server.set_accept_state()
-
- client = Connection(clientContext, clientSocket)
- client.set_connect_state()
-
- # Make them talk to each other.
- # self._interactInMemory(client, server)
- for i in range(3):
- for s in [client, server]:
- try:
- s.do_handshake()
- except WantReadError:
- pass
-
-
- def test_add_extra_chain_cert(self):
- """
- L{Context.add_extra_chain_cert} accepts an L{X509} instance to add to
- the certificate chain.
-
- See L{_create_certificate_chain} for the details of the certificate
- chain tested.
-
- The chain is tested by starting a server with scert and connecting
- to it with a client which trusts cacert and requires verification to
- succeed.
- """
- chain = _create_certificate_chain()
- [(cakey, cacert), (ikey, icert), (skey, scert)] = chain
-
- # Dump the CA certificate to a file because that's the only way to load
- # it as a trusted CA in the client context.
- for cert, name in [(cacert, 'ca.pem'), (icert, 'i.pem'), (scert, 's.pem')]:
- fObj = open(name, 'w')
- fObj.write(dump_certificate(FILETYPE_PEM, cert).decode('ascii'))
- fObj.close()
-
- for key, name in [(cakey, 'ca.key'), (ikey, 'i.key'), (skey, 's.key')]:
- fObj = open(name, 'w')
- fObj.write(dump_privatekey(FILETYPE_PEM, key).decode('ascii'))
- fObj.close()
-
- # Create the server context
- serverContext = Context(TLSv1_METHOD)
- serverContext.use_privatekey(skey)
- serverContext.use_certificate(scert)
- # The client already has cacert, we only need to give them icert.
- serverContext.add_extra_chain_cert(icert)
-
- # Create the client
- clientContext = Context(TLSv1_METHOD)
- clientContext.set_verify(
- VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb)
- clientContext.load_verify_locations('ca.pem')
-
- # Try it out.
- self._handshake_test(serverContext, clientContext)
-
-
- def test_use_certificate_chain_file(self):
- """
- L{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
- to it with a client which trusts cacert and requires verification to
- succeed.
- """
- chain = _create_certificate_chain()
- [(cakey, cacert), (ikey, icert), (skey, scert)] = chain
-
- # Write out the chain file.
- chainFile = self.mktemp()
- fObj = open(chainFile, 'w')
- # 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.close()
-
- serverContext = Context(TLSv1_METHOD)
- serverContext.use_certificate_chain_file(chainFile)
- serverContext.use_privatekey(skey)
-
- fObj = open('ca.pem', 'w')
- fObj.write(dump_certificate(FILETYPE_PEM, cacert).decode('ascii'))
- fObj.close()
-
- clientContext = Context(TLSv1_METHOD)
- clientContext.set_verify(
- VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb)
- clientContext.load_verify_locations('ca.pem')
-
- self._handshake_test(serverContext, clientContext)
-
- # 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
- arguments.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.get_verify_mode, None)
-
-
- def test_get_verify_mode(self):
- """
- L{Context.get_verify_mode} returns the verify mode flags previously
- passed to L{Context.set_verify}.
- """
- context = Context(TLSv1_METHOD)
- self.assertEquals(context.get_verify_mode(), 0)
- context.set_verify(
- 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.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.load_tmp_dh)
- self.assertRaises(TypeError, context.load_tmp_dh, "foo", None)
- self.assertRaises(TypeError, context.load_tmp_dh, object())
-
-
- def test_load_tmp_dh_missing_file(self):
- """
- L{Context.load_tmp_dh} raises L{OpenSSL.SSL.Error} if the specified file
- does not exist.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(Error, context.load_tmp_dh, "hello")
-
-
- def test_load_tmp_dh(self):
- """
- L{Context.load_tmp_dh} loads Diffie-Hellman parameters from the
- specified file.
- """
- context = Context(TLSv1_METHOD)
- dhfilename = self.mktemp()
- dhfile = open(dhfilename, "w")
- dhfile.write(dhparam)
- dhfile.close()
- context.load_tmp_dh(dhfilename)
- # XXX What should I assert here? -exarkun
-
-
- def test_set_cipher_list(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.
- """
- context = Context(TLSv1_METHOD)
- context.set_cipher_list("hello world:EXP-RC4-MD5")
- conn = Connection(context, None)
- self.assertEquals(conn.get_cipher_list(), ["EXP-RC4-MD5"])
-
-
-
-class ServerNameCallbackTests(TestCase, _LoopbackMixin):
- """
- Tests for L{Context.set_tlsext_servername_callback} and its interaction with
- L{Connection}.
- """
- def test_wrong_args(self):
- """
- L{Context.set_tlsext_servername_callback} raises L{TypeError} if called
- with other than one argument.
- """
- context = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, context.set_tlsext_servername_callback)
- 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
- callback, the one it replaces is dereferenced.
- """
- def callback(connection):
- pass
-
- def replacement(connection):
- pass
-
- context = Context(TLSv1_METHOD)
- context.set_tlsext_servername_callback(callback)
-
- tracker = ref(callback)
- del callback
-
- context.set_tlsext_servername_callback(replacement)
- collect()
- self.assertIdentical(None, tracker())
-
-
- 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}.
- """
- args = []
- def servername(conn):
- args.append((conn, conn.get_servername()))
- context = Context(TLSv1_METHOD)
- context.set_tlsext_servername_callback(servername)
-
- # Lose our reference to it. The Context is responsible for keeping it
- # alive now.
- del servername
- collect()
-
- # Necessary to actually accept the connection
- context.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
- context.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
-
- # Do a little connection to trigger the logic
- server = Connection(context, None)
- server.set_accept_state()
-
- client = Connection(Context(TLSv1_METHOD), None)
- client.set_connect_state()
-
- self._interactInMemory(server, client)
-
- self.assertEqual([(server, None)], args)
-
-
- 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.
- """
- args = []
- def servername(conn):
- args.append((conn, conn.get_servername()))
- context = Context(TLSv1_METHOD)
- context.set_tlsext_servername_callback(servername)
-
- # Necessary to actually accept the connection
- context.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
- context.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
-
- # Do a little connection to trigger the logic
- server = Connection(context, None)
- server.set_accept_state()
-
- client = Connection(Context(TLSv1_METHOD), None)
- client.set_connect_state()
- client.set_tlsext_host_name(b("foo1.example.com"))
-
- self._interactInMemory(server, client)
-
- self.assertEqual([(server, b("foo1.example.com"))], args)
-
-
-
-class ConnectionTests(TestCase, _LoopbackMixin):
- """
- Unit tests for L{OpenSSL.SSL.Connection}.
- """
- # XXX want_write
- # XXX want_read
- # XXX get_peer_certificate -> None
- # XXX sock_shutdown
- # XXX master_key -> TypeError
- # XXX server_random -> TypeError
- # XXX state_string
- # XXX connect -> TypeError
- # XXX connect_ex -> TypeError
- # XXX set_connect_state -> TypeError
- # XXX set_accept_state -> TypeError
- # XXX renegotiate_pending
- # XXX do_handshake -> TypeError
- # XXX bio_read -> TypeError
- # XXX recv -> TypeError
- # XXX send -> TypeError
- # XXX bio_write -> TypeError
-
- def test_type(self):
- """
- L{Connection} and L{ConnectionType} refer to the same type object and
- can be used to create instances of that type.
- """
- self.assertIdentical(Connection, ConnectionType)
- ctx = Context(TLSv1_METHOD)
- self.assertConsistentType(Connection, 'Connection', ctx, None)
-
-
- def test_get_context(self):
- """
- L{Connection.get_context} returns the L{Context} instance used to
- construct the L{Connection} instance.
- """
- context = Context(TLSv1_METHOD)
- connection = Connection(context, None)
- self.assertIdentical(connection.get_context(), context)
-
-
- def test_get_context_wrong_args(self):
- """
- L{Connection.get_context} raises L{TypeError} if called with any
- arguments.
- """
- connection = Connection(Context(TLSv1_METHOD), None)
- self.assertRaises(TypeError, connection.get_context, None)
-
-
- 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
- than 1.
- """
- ctx = Context(TLSv1_METHOD)
- connection = Connection(ctx, None)
- self.assertRaises(TypeError, connection.set_context)
- self.assertRaises(TypeError, connection.set_context, object())
- self.assertRaises(TypeError, connection.set_context, "hello")
- self.assertRaises(TypeError, connection.set_context, 1)
- self.assertRaises(TypeError, connection.set_context, 1, 2)
- self.assertRaises(
- TypeError, connection.set_context, Context(TLSv1_METHOD), 2)
- self.assertIdentical(ctx, connection.get_context())
-
-
- def test_set_context(self):
- """
- L{Connection.set_context} specifies a new L{Context} instance to be used
- for the connection.
- """
- original = Context(SSLv23_METHOD)
- replacement = Context(TLSv1_METHOD)
- connection = Connection(original, None)
- connection.set_context(replacement)
- self.assertIdentical(replacement, connection.get_context())
- # Lose our references to the contexts, just in case the Connection isn't
- # properly managing its own contributions to their reference counts.
- del original, replacement
- collect()
-
-
- def test_set_tlsext_host_name_wrong_args(self):
- """
- If L{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.
- """
- conn = Connection(Context(TLSv1_METHOD), None)
- self.assertRaises(TypeError, conn.set_tlsext_host_name)
- self.assertRaises(TypeError, conn.set_tlsext_host_name, object())
- self.assertRaises(TypeError, conn.set_tlsext_host_name, 123, 456)
- self.assertRaises(
- TypeError, conn.set_tlsext_host_name, b("with\0null"))
-
- if version_info >= (3,):
- # On Python 3.x, don't accidentally implicitly convert from text.
- self.assertRaises(
- TypeError,
- conn.set_tlsext_host_name, b("example.com").decode("ascii"))
-
-
- def test_get_servername_wrong_args(self):
- """
- L{Connection.get_servername} raises L{TypeError} if called with any
- arguments.
- """
- connection = Connection(Context(TLSv1_METHOD), None)
- self.assertRaises(TypeError, connection.get_servername, object())
- self.assertRaises(TypeError, connection.get_servername, 1)
- self.assertRaises(TypeError, connection.get_servername, "hello")
-
-
- def test_pending(self):
- """
- L{Connection.pending} returns the number of bytes available for
- immediate read.
- """
- connection = Connection(Context(TLSv1_METHOD), None)
- self.assertEquals(connection.pending(), 0)
-
-
- def test_pending_wrong_args(self):
- """
- L{Connection.pending} raises L{TypeError} if called with any arguments.
- """
- connection = Connection(Context(TLSv1_METHOD), None)
- self.assertRaises(TypeError, connection.pending, None)
-
-
- def test_connect_wrong_args(self):
- """
- L{Connection.connect} raises L{TypeError} if called with a non-address
- argument or with the wrong number of arguments.
- """
- connection = Connection(Context(TLSv1_METHOD), socket())
- self.assertRaises(TypeError, connection.connect, None)
- self.assertRaises(TypeError, connection.connect)
- self.assertRaises(TypeError, connection.connect, ("127.0.0.1", 1), None)
-
-
- def test_connect_refused(self):
- """
- L{Connection.connect} raises L{socket.error} if the underlying socket
- connect method raises it.
- """
- client = socket()
- context = Context(TLSv1_METHOD)
- clientSSL = Connection(context, client)
- exc = self.assertRaises(error, clientSSL.connect, ("127.0.0.1", 1))
- self.assertEquals(exc.args[0], ECONNREFUSED)
-
-
- def test_connect(self):
- """
- L{Connection.connect} establishes a connection to the specified address.
- """
- port = socket()
- port.bind(('', 0))
- port.listen(3)
-
- clientSSL = Connection(Context(TLSv1_METHOD), socket())
- clientSSL.connect(('127.0.0.1', port.getsockname()[1]))
- # XXX An assertion? Or something?
-
-
- if platform == "darwin":
- "connect_ex sometimes causes a kernel panic on OS X 10.6.4"
- else:
- def test_connect_ex(self):
- """
- If there is a connection error, L{Connection.connect_ex} returns the
- errno instead of raising an exception.
- """
- port = socket()
- port.bind(('', 0))
- port.listen(3)
-
- clientSSL = Connection(Context(TLSv1_METHOD), socket())
- clientSSL.setblocking(False)
- result = clientSSL.connect_ex(port.getsockname())
- expected = (EINPROGRESS, EWOULDBLOCK)
- self.assertTrue(
- result in expected, "%r not in %r" % (result, expected))
-
-
- def test_accept_wrong_args(self):
- """
- L{Connection.accept} raises L{TypeError} if called with any arguments.
- """
- connection = Connection(Context(TLSv1_METHOD), socket())
- self.assertRaises(TypeError, connection.accept, None)
-
-
- 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
- connection originated from.
- """
- ctx = Context(TLSv1_METHOD)
- ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
- ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
- port = socket()
- portSSL = Connection(ctx, port)
- portSSL.bind(('', 0))
- portSSL.listen(3)
-
- clientSSL = Connection(Context(TLSv1_METHOD), socket())
-
- # Calling portSSL.getsockname() here to get the server IP address sounds
- # great, but frequently fails on Windows.
- clientSSL.connect(('127.0.0.1', portSSL.getsockname()[1]))
-
- serverSSL, address = portSSL.accept()
-
- self.assertTrue(isinstance(serverSSL, Connection))
- self.assertIdentical(serverSSL.get_context(), ctx)
- self.assertEquals(address, clientSSL.getsockname())
-
-
- def test_shutdown_wrong_args(self):
- """
- L{Connection.shutdown} raises L{TypeError} if called with the wrong
- number of arguments or with arguments other than integers.
- """
- connection = Connection(Context(TLSv1_METHOD), None)
- self.assertRaises(TypeError, connection.shutdown, None)
- self.assertRaises(TypeError, connection.get_shutdown, None)
- self.assertRaises(TypeError, connection.set_shutdown)
- self.assertRaises(TypeError, connection.set_shutdown, None)
- self.assertRaises(TypeError, connection.set_shutdown, 0, 1)
-
-
- def test_shutdown(self):
- """
- L{Connection.shutdown} performs an SSL-level connection shutdown.
- """
- server, client = self._loopback()
- self.assertFalse(server.shutdown())
- self.assertEquals(server.get_shutdown(), SENT_SHUTDOWN)
- self.assertRaises(ZeroReturnError, client.recv, 1024)
- self.assertEquals(client.get_shutdown(), RECEIVED_SHUTDOWN)
- client.shutdown()
- self.assertEquals(client.get_shutdown(), SENT_SHUTDOWN|RECEIVED_SHUTDOWN)
- self.assertRaises(ZeroReturnError, server.recv, 1024)
- self.assertEquals(server.get_shutdown(), SENT_SHUTDOWN|RECEIVED_SHUTDOWN)
-
-
- def test_set_shutdown(self):
- """
- L{Connection.set_shutdown} sets the state of the SSL connection shutdown
- process.
- """
- connection = Connection(Context(TLSv1_METHOD), socket())
- connection.set_shutdown(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
- with any arguments.
- """
- conn = Connection(Context(TLSv1_METHOD), None)
- self.assertRaises(TypeError, conn.get_app_data, None)
- self.assertRaises(TypeError, conn.set_app_data)
- self.assertRaises(TypeError, conn.set_app_data, None, None)
-
-
- 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}.
- """
- conn = Connection(Context(TLSv1_METHOD), None)
- app_data = object()
- conn.set_app_data(app_data)
- self.assertIdentical(conn.get_app_data(), app_data)
-
-
- def test_makefile(self):
- """
- L{Connection.makefile} is not implemented and calling that method raises
- L{NotImplementedError}.
- """
- conn = Connection(Context(TLSv1_METHOD), None)
- self.assertRaises(NotImplementedError, conn.makefile)
-
-
- def test_get_peer_cert_chain_wrong_args(self):
- """
- L{Connection.get_peer_cert_chain} raises L{TypeError} if called with any
- arguments.
- """
- conn = Connection(Context(TLSv1_METHOD), None)
- self.assertRaises(TypeError, conn.get_peer_cert_chain, 1)
- self.assertRaises(TypeError, conn.get_peer_cert_chain, "foo")
- self.assertRaises(TypeError, conn.get_peer_cert_chain, object())
- self.assertRaises(TypeError, conn.get_peer_cert_chain, [])
-
-
- def test_get_peer_cert_chain(self):
- """
- L{Connection.get_peer_cert_chain} returns a list of certificates which
- the connected server returned for the certification verification.
- """
- chain = _create_certificate_chain()
- [(cakey, cacert), (ikey, icert), (skey, scert)] = chain
-
- serverContext = Context(TLSv1_METHOD)
- serverContext.use_privatekey(skey)
- serverContext.use_certificate(scert)
- serverContext.add_extra_chain_cert(icert)
- serverContext.add_extra_chain_cert(cacert)
- server = Connection(serverContext, None)
- server.set_accept_state()
-
- # Create the client
- clientContext = Context(TLSv1_METHOD)
- clientContext.set_verify(VERIFY_NONE, verify_cb)
- client = Connection(clientContext, None)
- client.set_connect_state()
-
- self._interactInMemory(client, server)
-
- chain = client.get_peer_cert_chain()
- self.assertEqual(len(chain), 3)
- self.assertEqual(
- "Server Certificate", chain[0].get_subject().CN)
- self.assertEqual(
- "Intermediate Certificate", chain[1].get_subject().CN)
- self.assertEqual(
- "Authority Certificate", chain[2].get_subject().CN)
-
-
- def test_get_peer_cert_chain_none(self):
- """
- L{Connection.get_peer_cert_chain} returns C{None} if the peer sends no
- certificate chain.
- """
- 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, None)
- server.set_accept_state()
- client = Connection(Context(TLSv1_METHOD), None)
- client.set_connect_state()
- self._interactInMemory(client, server)
- self.assertIdentical(None, server.get_peer_cert_chain())
-
-
-
-class ConnectionGetCipherListTests(TestCase):
- """
- Tests for L{Connection.get_cipher_list}.
- """
- def test_wrong_args(self):
- """
- L{Connection.get_cipher_list} raises L{TypeError} if called with any
- arguments.
- """
- connection = Connection(Context(TLSv1_METHOD), None)
- self.assertRaises(TypeError, connection.get_cipher_list, None)
-
-
- 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.
- """
- connection = Connection(Context(TLSv1_METHOD), None)
- ciphers = connection.get_cipher_list()
- self.assertTrue(isinstance(ciphers, list))
- for cipher in ciphers:
- self.assertTrue(isinstance(cipher, str))
-
-
-
-class ConnectionSendTests(TestCase, _LoopbackMixin):
- """
- Tests for L{Connection.send}
- """
- def test_wrong_args(self):
- """
- When called with arguments other than a single string,
- L{Connection.send} raises L{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")
-
-
- def test_short_bytes(self):
- """
- When passed a short byte string, L{Connection.send} transmits all of it
- and returns the number of bytes sent.
- """
- server, client = self._loopback()
- count = server.send(b('xy'))
- self.assertEquals(count, 2)
- self.assertEquals(client.recv(2), b('xy'))
-
- try:
- memoryview
- except NameError:
- "cannot test sending memoryview without memoryview"
- else:
- 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
- bytes sent.
- """
- server, client = self._loopback()
- count = server.send(memoryview(b('xy')))
- self.assertEquals(count, 2)
- self.assertEquals(client.recv(2), b('xy'))
-
-
-
-class ConnectionSendallTests(TestCase, _LoopbackMixin):
- """
- Tests for L{Connection.sendall}.
- """
- def test_wrong_args(self):
- """
- When called with arguments other than a single string,
- L{Connection.sendall} raises L{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")
-
-
- def test_short(self):
- """
- L{Connection.sendall} transmits all of the bytes in the string passed to
- it.
- """
- server, client = self._loopback()
- server.sendall(b('x'))
- self.assertEquals(client.recv(1), b('x'))
-
-
- try:
- memoryview
- except NameError:
- "cannot test sending memoryview without memoryview"
- else:
- def test_short_memoryview(self):
- """
- When passed a memoryview onto a small number of bytes,
- L{Connection.sendall} transmits all of them.
- """
- server, client = self._loopback()
- server.sendall(memoryview(b('x')))
- self.assertEquals(client.recv(1), b('x'))
-
-
- def test_long(self):
- """
- L{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()
- # Should be enough, underlying SSL_write should only do 16k at a time.
- # On Windows, after 32k of bytes the write will block (forever - because
- # no one is yet reading).
- message = b('x') * (1024 * 32 - 1) + b('y')
- server.sendall(message)
- accum = []
- received = 0
- while received < len(message):
- data = client.recv(1024)
- accum.append(data)
- received += len(data)
- self.assertEquals(message, b('').join(accum))
-
-
- def test_closed(self):
- """
- If the underlying socket is closed, L{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")
-
-
-
-class ConnectionRenegotiateTests(TestCase, _LoopbackMixin):
- """
- Tests for SSL renegotiation APIs.
- """
- def test_renegotiate_wrong_args(self):
- """
- L{Connection.renegotiate} raises L{TypeError} if called with any
- arguments.
- """
- connection = Connection(Context(TLSv1_METHOD), None)
- self.assertRaises(TypeError, connection.renegotiate, None)
-
-
- def test_total_renegotiations_wrong_args(self):
- """
- L{Connection.total_renegotiations} raises L{TypeError} if called with
- any arguments.
- """
- connection = Connection(Context(TLSv1_METHOD), None)
- self.assertRaises(TypeError, connection.total_renegotiations, None)
-
-
- def test_total_renegotiations(self):
- """
- L{Connection.total_renegotiations} returns C{0} before any
- renegotiations have happened.
- """
- connection = Connection(Context(TLSv1_METHOD), None)
- self.assertEquals(connection.total_renegotiations(), 0)
-
-
-# def test_renegotiate(self):
-# """
-# """
-# server, client = self._loopback()
-
-# server.send("hello world")
-# self.assertEquals(client.recv(len("hello world")), "hello world")
-
-# self.assertEquals(server.total_renegotiations(), 0)
-# self.assertTrue(server.renegotiate())
-
-# server.setblocking(False)
-# client.setblocking(False)
-# while server.renegotiate_pending():
-# client.do_handshake()
-# server.do_handshake()
-
-# self.assertEquals(server.total_renegotiations(), 1)
-
-
-
-
-class ErrorTests(TestCase):
- """
- Unit tests for L{OpenSSL.SSL.Error}.
- """
- def test_type(self):
- """
- L{Error} is an exception type.
- """
- self.assertTrue(issubclass(Error, Exception))
- self.assertEqual(Error.__name__, 'Error')
-
-
-
-class ConstantsTests(TestCase):
- """
- Tests for the values of constants exposed in L{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
- their values.
- """
- # unittest.TestCase has no skip mechanism
- 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}.
- """
- self.assertEqual(OP_NO_QUERY_MTU, 0x1000)
- else:
- "OP_NO_QUERY_MTU unavailable - OpenSSL version may be too old"
-
-
- 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}.
- """
- self.assertEqual(OP_COOKIE_EXCHANGE, 0x2000)
- else:
- "OP_COOKIE_EXCHANGE unavailable - OpenSSL version may be too old"
-
-
- 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}.
- """
- self.assertEqual(OP_NO_TICKET, 0x4000)
- else:
- "OP_NO_TICKET unavailable - OpenSSL version may be too old"
-
-
-
-class MemoryBIOTests(TestCase, _LoopbackMixin):
- """
- Tests for L{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 the server side Connection. This is mostly setup boilerplate
- # - use TLSv1, use a particular certificate, etc.
- server_ctx = Context(TLSv1_METHOD)
- server_ctx.set_options(OP_NO_SSLv2 | OP_NO_SSLv3 | OP_SINGLE_DH_USE )
- server_ctx.set_verify(VERIFY_PEER|VERIFY_FAIL_IF_NO_PEER_CERT|VERIFY_CLIENT_ONCE, verify_cb)
- server_store = server_ctx.get_cert_store()
- server_ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
- server_ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
- server_ctx.check_privatekey()
- server_store.add_cert(load_certificate(FILETYPE_PEM, root_cert_pem))
- # Here the Connection is actually created. If None is passed as the 2nd
- # parameter, it indicates a memory BIO should be created.
- server_conn = Connection(server_ctx, sock)
- server_conn.set_accept_state()
- return server_conn
-
-
- def _client(self, sock):
- """
- Create a new client-side SSL L{Connection} object wrapped around
- C{sock}.
- """
- # Now create the client side Connection. Similar boilerplate to the
- # above.
- client_ctx = Context(TLSv1_METHOD)
- client_ctx.set_options(OP_NO_SSLv2 | OP_NO_SSLv3 | OP_SINGLE_DH_USE )
- client_ctx.set_verify(VERIFY_PEER|VERIFY_FAIL_IF_NO_PEER_CERT|VERIFY_CLIENT_ONCE, verify_cb)
- client_store = client_ctx.get_cert_store()
- client_ctx.use_privatekey(load_privatekey(FILETYPE_PEM, client_key_pem))
- client_ctx.use_certificate(load_certificate(FILETYPE_PEM, client_cert_pem))
- client_ctx.check_privatekey()
- client_store.add_cert(load_certificate(FILETYPE_PEM, root_cert_pem))
- client_conn = Connection(client_ctx, sock)
- client_conn.set_connect_state()
- return client_conn
-
-
- def test_memoryConnect(self):
- """
- Two L{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.
- """
- server_conn = self._server(None)
- client_conn = self._client(None)
-
- # There should be no key or nonces yet.
- self.assertIdentical(server_conn.master_key(), None)
- self.assertIdentical(server_conn.client_random(), None)
- self.assertIdentical(server_conn.server_random(), None)
-
- # First, the handshake needs to happen. We'll deliver bytes back and
- # forth between the client and server until neither of them feels like
- # speaking any more.
- self.assertIdentical(
- self._interactInMemory(client_conn, server_conn), None)
-
- # Now that the handshake is done, there should be a key and nonces.
- self.assertNotIdentical(server_conn.master_key(), None)
- self.assertNotIdentical(server_conn.client_random(), None)
- self.assertNotIdentical(server_conn.server_random(), None)
- self.assertEquals(server_conn.client_random(), client_conn.client_random())
- self.assertEquals(server_conn.server_random(), client_conn.server_random())
- self.assertNotEquals(server_conn.client_random(), server_conn.server_random())
- self.assertNotEquals(client_conn.client_random(), client_conn.server_random())
-
- # Here are the bytes we'll try to send.
- important_message = b('One if by land, two if by sea.')
-
- server_conn.write(important_message)
- self.assertEquals(
- self._interactInMemory(client_conn, server_conn),
- (client_conn, important_message))
-
- client_conn.write(important_message[::-1])
- self.assertEquals(
- self._interactInMemory(client_conn, server_conn),
- (server_conn, important_message[::-1]))
-
-
- def test_socketConnect(self):
- """
- Just like L{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
- 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.
- """
- server_conn, client_conn = self._loopback()
-
- important_message = b("Help me Obi Wan Kenobi, you're my only hope.")
- client_conn.send(important_message)
- msg = server_conn.recv(1024)
- self.assertEqual(msg, important_message)
-
- # Again in the other direction, just for fun.
- important_message = important_message[::-1]
- server_conn.send(important_message)
- msg = client_conn.recv(1024)
- self.assertEqual(msg, important_message)
-
-
- 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.
- """
- context = Context(SSLv3_METHOD)
- client = socket()
- clientSSL = Connection(context, client)
- self.assertRaises( TypeError, clientSSL.bio_read, 100)
- self.assertRaises( TypeError, clientSSL.bio_write, "foo")
- self.assertRaises( TypeError, clientSSL.bio_shutdown )
-
-
- 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
- returned and that many bytes from the beginning of the input can be
- read from the other end of the connection.
- """
- server = self._server(None)
- client = self._client(None)
-
- self._interactInMemory(client, server)
-
- size = 2 ** 15
- sent = client.send("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.
- self.assertTrue(sent < size)
-
- receiver, received = self._interactInMemory(client, server)
- self.assertIdentical(receiver, server)
-
- # We can rely on all of these bytes being received at once because
- # _loopback passes 2 ** 16 to recv - more than 2 ** 15.
- self.assertEquals(len(received), sent)
-
-
- def test_shutdown(self):
- """
- L{Connection.bio_shutdown} signals the end of the data stream from
- which the L{Connection} reads.
- """
- server = self._server(None)
- server.bio_shutdown()
- e = self.assertRaises(Error, server.recv, 1024)
- # We don't want WantReadError or ZeroReturnError or anything - it's a
- # handshake failure.
- self.assertEquals(e.__class__, Error)
-
-
- def _check_client_ca_list(self, func):
- """
- Verify the return value of the C{get_client_ca_list} method for server and client connections.
-
- @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
- times.
- """
- server = self._server(None)
- client = self._client(None)
- self.assertEqual(client.get_client_ca_list(), [])
- self.assertEqual(server.get_client_ca_list(), [])
- ctx = server.get_context()
- expected = func(ctx)
- self.assertEqual(client.get_client_ca_list(), [])
- self.assertEqual(server.get_client_ca_list(), expected)
- self._interactInMemory(client, server)
- self.assertEqual(client.get_client_ca_list(), expected)
- self.assertEqual(server.get_client_ca_list(), expected)
-
-
- def test_set_client_ca_list_errors(self):
- """
- L{Context.set_client_ca_list} raises a L{TypeError} if called with a
- non-list or a list that contains objects other than X509Names.
- """
- ctx = Context(TLSv1_METHOD)
- self.assertRaises(TypeError, ctx.set_client_ca_list, "spam")
- self.assertRaises(TypeError, ctx.set_client_ca_list, ["spam"])
- self.assertIdentical(ctx.set_client_ca_list([]), None)
-
-
- def test_set_empty_ca_list(self):
- """
- If passed an empty list, L{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
- after the connection is set up.
- """
- def no_ca(ctx):
- ctx.set_client_ca_list([])
- return []
- self._check_client_ca_list(no_ca)
-
-
- 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
- name to the client and, on both the server and client sides,
- L{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)
- cadesc = cacert.get_subject()
- def single_ca(ctx):
- ctx.set_client_ca_list([cadesc])
- return [cadesc]
- self._check_client_ca_list(single_ca)
-
-
- 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
- names to the client and, on both the server and client sides,
- L{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)
- clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
-
- sedesc = secert.get_subject()
- cldesc = clcert.get_subject()
-
- def multiple_ca(ctx):
- L = [sedesc, cldesc]
- ctx.set_client_ca_list(L)
- return L
- self._check_client_ca_list(multiple_ca)
-
-
- 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
- sent to the client.
- """
- cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
- secert = load_certificate(FILETYPE_PEM, server_cert_pem)
- clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
-
- cadesc = cacert.get_subject()
- sedesc = secert.get_subject()
- cldesc = clcert.get_subject()
-
- def changed_ca(ctx):
- ctx.set_client_ca_list([sedesc, cldesc])
- ctx.set_client_ca_list([cadesc])
- return [cadesc]
- self._check_client_ca_list(changed_ca)
-
-
- def test_mutated_ca_list(self):
- """
- If the list passed to L{Context.set_client_ca_list} is mutated
- afterwards, this does not affect the list of CA names sent to the
- client.
- """
- cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
- secert = load_certificate(FILETYPE_PEM, server_cert_pem)
-
- cadesc = cacert.get_subject()
- sedesc = secert.get_subject()
-
- def mutated_ca(ctx):
- L = [cadesc]
- ctx.set_client_ca_list([cadesc])
- L.append(sedesc)
- return [cadesc]
- self._check_client_ca_list(mutated_ca)
-
-
- def test_add_client_ca_errors(self):
- """
- L{Context.add_client_ca} raises L{TypeError} if called with a non-X509
- object or with a number of arguments other than one.
- """
- ctx = Context(TLSv1_METHOD)
- cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
- self.assertRaises(TypeError, ctx.add_client_ca)
- self.assertRaises(TypeError, ctx.add_client_ca, "spam")
- self.assertRaises(TypeError, ctx.add_client_ca, cacert, cacert)
-
-
- 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}.
- """
- cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
- cadesc = cacert.get_subject()
- def single_ca(ctx):
- ctx.add_client_ca(cacert)
- return [cadesc]
- self._check_client_ca_list(single_ca)
-
-
- 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.
- """
- cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
- secert = load_certificate(FILETYPE_PEM, server_cert_pem)
-
- cadesc = cacert.get_subject()
- sedesc = secert.get_subject()
-
- def multiple_ca(ctx):
- ctx.add_client_ca(cacert)
- ctx.add_client_ca(secert)
- return [cadesc, sedesc]
- self._check_client_ca_list(multiple_ca)
-
-
- 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
- call and the CA name from the second call.
- """
- cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
- secert = load_certificate(FILETYPE_PEM, server_cert_pem)
- clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
-
- cadesc = cacert.get_subject()
- sedesc = secert.get_subject()
- cldesc = clcert.get_subject()
-
- def mixed_set_add_ca(ctx):
- ctx.set_client_ca_list([cadesc, sedesc])
- ctx.add_client_ca(clcert)
- return [cadesc, sedesc, cldesc]
- self._check_client_ca_list(mixed_set_add_ca)
-
-
- 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
- call with the names specified by the latter cal.
- """
- cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
- secert = load_certificate(FILETYPE_PEM, server_cert_pem)
- clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
-
- cadesc = cacert.get_subject()
- sedesc = secert.get_subject()
-
- def set_replaces_add_ca(ctx):
- ctx.add_client_ca(clcert)
- ctx.set_client_ca_list([cadesc])
- ctx.add_client_ca(secert)
- return [cadesc, sedesc]
- self._check_client_ca_list(set_replaces_add_ca)
-
-
-class InfoConstantTests(TestCase):
- """
- Tests for assorted constants exposed for use in info callbacks.
- """
- def test_integers(self):
- """
- All of the info constants are integers.
-
- This is a very weak test. It would be nice to have one that actually
- verifies that as certain info events happen, the value passed to the
- info callback matches up with the constant exposed by OpenSSL.SSL.
- """
- for const in [
- SSL_ST_CONNECT, SSL_ST_ACCEPT, SSL_ST_MASK, SSL_ST_INIT,
- SSL_ST_BEFORE, SSL_ST_OK, SSL_ST_RENEGOTIATE,
- SSL_CB_LOOP, SSL_CB_EXIT, SSL_CB_READ, SSL_CB_WRITE, SSL_CB_ALERT,
- SSL_CB_READ_ALERT, SSL_CB_WRITE_ALERT, SSL_CB_ACCEPT_LOOP,
- SSL_CB_ACCEPT_EXIT, SSL_CB_CONNECT_LOOP, SSL_CB_CONNECT_EXIT,
- SSL_CB_HANDSHAKE_START, SSL_CB_HANDSHAKE_DONE]:
-
- self.assertTrue(isinstance(const, int))
-
-
-if __name__ == '__main__':
- main()
+++ /dev/null
-# Copyright (C) Jean-Paul Calderone
-# Copyright (C) Twisted Matrix Laboratories.
-# See LICENSE for details.
-
-"""
-Helpers for the OpenSSL test suite, largely copied from
-U{Twisted<http://twistedmatrix.com/>}.
-"""
-
-import shutil
-import os, os.path
-from tempfile import mktemp
-from unittest import TestCase
-import sys
-
-from OpenSSL.crypto import Error, _exception_from_error_queue
-
-if sys.version_info < (3, 0):
- def b(s):
- return s
- bytes = str
-else:
- def b(s):
- return s.encode("charmap")
- bytes = bytes
-
-
-class TestCase(TestCase):
- """
- L{TestCase} adds useful testing functionality beyond what is available
- from the standard library L{unittest.TestCase}.
- """
- def tearDown(self):
- """
- Clean up any files or directories created using L{TestCase.mktemp}.
- Subclasses must invoke this method if they override it or the
- cleanup will not occur.
- """
- if False and self._temporaryFiles is not None:
- for temp in self._temporaryFiles:
- if os.path.isdir(temp):
- shutil.rmtree(temp)
- elif os.path.exists(temp):
- os.unlink(temp)
- try:
- _exception_from_error_queue()
- except Error:
- e = sys.exc_info()[1]
- if e.args != ([],):
- self.fail("Left over errors in OpenSSL error queue: " + repr(e))
-
-
- def failUnlessIn(self, containee, container, msg=None):
- """
- Fail the test if C{containee} is not found in C{container}.
-
- @param containee: the value that should be in C{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
- '%r not in %r' % (first, second)
- """
- if containee not in container:
- raise self.failureException(msg or "%r not in %r"
- % (containee, container))
- return containee
- assertIn = failUnlessIn
-
- def failUnlessIdentical(self, first, second, msg=None):
- """
- Fail the test if C{first} is not C{second}. This is an
- obect-identity-equality test, not an object equality
- (i.e. C{__eq__}) test.
-
- @param msg: if msg is None, then the failure message will be
- '%r is not %r' % (first, second)
- """
- if first is not second:
- raise self.failureException(msg or '%r is not %r' % (first, second))
- return first
- assertIdentical = failUnlessIdentical
-
-
- def failIfIdentical(self, first, second, msg=None):
- """
- Fail the test if C{first} is C{second}. This is an
- obect-identity-equality test, not an object equality
- (i.e. C{__eq__}) test.
-
- @param msg: if msg is None, then the failure message will be
- '%r is %r' % (first, second)
- """
- if first is second:
- raise self.failureException(msg or '%r is %r' % (first, second))
- return first
- assertNotIdentical = failIfIdentical
-
-
- 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.
-
- @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
- not raise an exception or if it raises an exception of a
- different type.
- """
- try:
- result = f(*args, **kwargs)
- except exception:
- inst = sys.exc_info()[1]
- return inst
- except:
- raise self.failureException('%s raised instead of %s'
- % (sys.exc_info()[0],
- exception.__name__,
- ))
- else:
- raise self.failureException('%s not raised (%r returned)'
- % (exception.__name__, result))
- assertRaises = failUnlessRaises
-
-
- _temporaryFiles = None
- def mktemp(self):
- """
- Pathetic substitute for twisted.trial.unittest.TestCase.mktemp.
- """
- if self._temporaryFiles is None:
- self._temporaryFiles = []
- temp = 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
- 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
- create an instance of it.
- """
- self.assertEqual(theType.__name__, name)
- self.assertTrue(isinstance(theType, type))
- instance = theType(*constructionArgs)
- self.assertIdentical(type(instance), theType)
+++ /dev/null
-from OpenSSL import SSL
-_ssl = SSL
-del SSL
-
-import threading
-_RLock = threading.RLock
-del threading
-
-class Connection:
- def __init__(self, *args):
- self._ssl_conn = apply(_ssl.Connection, args)
- self._lock = _RLock()
-
- for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read',
- 'renegotiate', 'bind', 'listen', 'connect', 'accept',
- 'setblocking', 'fileno', 'shutdown', 'close', 'get_cipher_list',
- 'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
- 'makefile', 'get_app_data', 'set_app_data', 'state_string',
- 'sock_shutdown', 'get_peer_certificate', 'get_peer_cert_chain', 'want_read',
- 'want_write', 'set_connect_state', 'set_accept_state',
- 'connect_ex', 'sendall'):
- exec("""def %s(self, *args):
- self._lock.acquire()
- try:
- return self._ssl_conn.%s(*args)
- finally:
- self._lock.release()\n""" % (f, f))
-
+++ /dev/null
-/*
- * 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 <Python.h>
-#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
+++ /dev/null
-/*
- * 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 <Python.h>
-#include <openssl/err.h>
-
-/*
- * 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
+++ /dev/null
-# Copyright (C) AB Strakt
-# Copyright (C) Jean-Paul Calderone
-# See LICENSE for details.
-
-"""
-pyOpenSSL - A simple wrapper around the OpenSSL library
-"""
-
-__version__ = '0.13'
-Metadata-Version: 1.0
+Metadata-Version: 2.1
Name: pyOpenSSL
-Version: 0.13
+Version: 19.0.0
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
+Home-page: https://pyopenssl.org/
+Author: The pyOpenSSL developers
+Author-email: cryptography-dev@python.org
+Maintainer: Hynek Schlawack
+Maintainer-email: hs@ox.cx
+License: Apache License, Version 2.0
+Description: ========================================================
+ pyOpenSSL -- A Python wrapper around the OpenSSL library
+ ========================================================
+
+ .. image:: https://readthedocs.org/projects/pyopenssl/badge/?version=stable
+ :target: https://pyopenssl.org/en/stable/
+ :alt: Stable Docs
+
+ .. image:: https://travis-ci.org/pyca/pyopenssl.svg?branch=master
+ :target: https://travis-ci.org/pyca/pyopenssl
+ :alt: Build status
+
+ .. image:: https://codecov.io/github/pyca/pyopenssl/branch/master/graph/badge.svg
+ :target: https://codecov.io/github/pyca/pyopenssl
+ :alt: Test coverage
+
+ **Note:** The Python Cryptographic Authority **strongly suggests** the use of `pyca/cryptography`_
+ where possible. If you are using pyOpenSSL for anything other than making a TLS connection
+ **you should move to cryptography and drop your pyOpenSSL dependency**.
+
+ 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 ;)
+
+ ... and much more.
+
+ You can find more information in the documentation_.
+ Development takes place on GitHub_.
+
+
+ Discussion
+ ==========
+
+ If you run into bugs, you can file them in our `issue tracker`_.
+
+ We maintain a cryptography-dev_ mailing list for both user and development discussions.
+
+ You can also join ``#cryptography-dev`` on Freenode to ask questions or get involved.
+
+
+ .. _documentation: https://pyopenssl.org/
+ .. _`issue tracker`: https://github.com/pyca/pyopenssl/issues
+ .. _cryptography-dev: https://mail.python.org/mailman/listinfo/cryptography-dev
+ .. _GitHub: https://github.com/pyca/pyopenssl
+ .. _`pyca/cryptography`: https://github.com/pyca/cryptography
+
+
+ Release Information
+ ===================
+
+ 19.0.0 (2019-01-21)
+ -------------------
+
+
+ Backward-incompatible changes:
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ - ``X509Store.add_cert`` no longer raises an error if you add a duplicate cert.
+ `#787 <https://github.com/pyca/pyopenssl/pull/787>`_
+
+
+ Deprecations:
+ ^^^^^^^^^^^^^
+
+ *none*
+
+
+ Changes:
+ ^^^^^^^^
+
+ - pyOpenSSL now works with OpenSSL 1.1.1.
+ `#805 <https://github.com/pyca/pyopenssl/pull/805>`_
+ - pyOpenSSL now handles NUL bytes in ``X509Name.get_components()``
+ `#804 <https://github.com/pyca/pyopenssl/pull/804>`_
+
+
+ `Full changelog <https://pyopenssl.org/en/stable/changelog.html>`_.
+
+
Platform: UNKNOWN
+Classifier: Development Status :: 6 - Mature
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: POSIX
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Security :: Cryptography
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Networking
+Provides-Extra: docs
+Provides-Extra: test
+++ /dev/null
-
-pyOpenSSL - A Python wrapper around the OpenSSL library
-------------------------------------------------------------------------------
-
-See the file INSTALL for installation instructions.
-
-I appreciate bug reports and patches. Please visit
-<http://bugs.launchpad.net/pyopenssl>.
--- /dev/null
+========================================================
+pyOpenSSL -- A Python wrapper around the OpenSSL library
+========================================================
+
+.. image:: https://readthedocs.org/projects/pyopenssl/badge/?version=stable
+ :target: https://pyopenssl.org/en/stable/
+ :alt: Stable Docs
+
+.. image:: https://travis-ci.org/pyca/pyopenssl.svg?branch=master
+ :target: https://travis-ci.org/pyca/pyopenssl
+ :alt: Build status
+
+.. image:: https://codecov.io/github/pyca/pyopenssl/branch/master/graph/badge.svg
+ :target: https://codecov.io/github/pyca/pyopenssl
+ :alt: Test coverage
+
+**Note:** The Python Cryptographic Authority **strongly suggests** the use of `pyca/cryptography`_
+where possible. If you are using pyOpenSSL for anything other than making a TLS connection
+**you should move to cryptography and drop your pyOpenSSL dependency**.
+
+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.
+
+You can find more information in the documentation_.
+Development takes place on GitHub_.
+
+
+Discussion
+==========
+
+If you run into bugs, you can file them in our `issue tracker`_.
+
+We maintain a cryptography-dev_ mailing list for both user and development discussions.
+
+You can also join ``#cryptography-dev`` on Freenode to ask questions or get involved.
+
+
+.. _documentation: https://pyopenssl.org/
+.. _`issue tracker`: https://github.com/pyca/pyopenssl/issues
+.. _cryptography-dev: https://mail.python.org/mailman/listinfo/cryptography-dev
+.. _GitHub: https://github.com/pyca/pyopenssl
+.. _`pyca/cryptography`: https://github.com/pyca/cryptography
+++ /dev/null
-TODO list
-
-* Think more carefully about the relation between X509 and X509_NAME
- _set_{subject,issuer} dup the new name and free the old one.
-* Consider Pyrex
-* Updated docs! (rpm, ...)
-* _Somehow_ get makefile to work!
-* httpslib, imapslib, ftpslib?
--- /dev/null
+This file only contains the changes up to release 0.15.1. Newer changes can be
+found at <https://pyopenssl.readthedocs.io/en/latest/changelog.html>.
+
+***
+
+2015-04-14 Hynek Schlawack <hs@ox.cx>
+
+ * Release 0.15.1
+
+2015-04-14 Glyph Lefkowitz <glyph@twistedmatrix.com>
+
+ * OpenSSL/SSL.py, OpenSSL/test/test_ssl.py: Fix a regression
+ present in 0.15, where when an error occurs and no errno() is set,
+ a KeyError is raised. This happens, for example, if
+ Connection.shutdown() is called when the underlying transport has
+ gone away.
+
+2015-04-14 Hynek Schlawack <hs@ox.cx>
+
+ * Release 0.15
+
+2015-04-12 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/rand.py, OpenSSL/SSL.py: APIs which previously accepted
+ filenames only as bytes now accept them as either bytes or
+ unicode (and respect sys.getfilesystemencoding()).
+
+2015-03-23 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/SSL.py: Add Cory Benfield's next-protocol-negotiation
+ (NPN) bindings.
+
+2015-03-15 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/SSL.py: Add ``Connection.recv_into``, mirroring the
+ builtin ``socket.recv_into``. Based on work from Cory Benfield.
+ * OpenSSL/test/test_ssl.py: Add tests for ``recv_into``.
+
+2015-01-30 Stephen Holsapple <sholsapp@gmail.com>
+
+ * OpenSSL/crypto.py: Expose ``X509StoreContext`` for verifying certificates.
+ * OpenSSL/test/test_crypto.py: Add intermediate certificates for
+
+2015-01-08 Paul Aurich <paul@darkrain42.org>
+
+ * OpenSSL/SSL.py: ``Connection.shutdown`` now propagates errors from the
+ underlying socket.
+
+2014-12-11 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/SSL.py: Fixed a regression ``Context.check_privatekey``
+ causing it to always succeed - even if it should fail.
+
+2014-08-21 Alex Gaynor <alex.gaynor@gmail.com>
+
+ * OpenSSL/crypto.py: Fixed a regression where calling ``load_pkcs7_data``
+ with ``FILETYPE_ASN1`` would fail with a ``NameError``.
+
+2014-05-05 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/SSL.py: Fix a regression in which the first argument of
+ the "verify" callback was incorrectly passed a ``Context`` instance
+ instead of the ``Connection`` instance.
+ * OpenSSL/test/test_ssl.py: Add a test for the value passed as the
+ first argument of the "verify" callback.
+
+2014-04-19 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/crypto.py: Based on work from Alex Gaynor, Andrew
+ Lutomirski, Tobias Oberstein, Laurens Van Houtven, and Hynek
+ Schlawack, add ``get_elliptic_curve`` and ``get_elliptic_curves``
+ to support TLS ECDHE modes.
+ * OpenSSL/SSL.py: Add ``Context.set_tmp_ecdh`` to configure a TLS
+ context with a particular elliptic curve for ECDHE modes.
+
+2014-04-19 Markus Unterwaditzer <markus@unterwaditzer.net>
+
+ * OpenSSL/SSL.py: ``Connection.send`` and ``Connection.sendall``
+ now also accept the ``buffer`` type as data.
+
+2014-04-05 Stephen Holsapple <sholsapp@gmail.com>
+
+ * OpenSSL/crypto.py: Make ``load_pkcs12`` backwards compatible with
+ pyOpenSSL 0.13 by making passphrase optional.
+
+2014-03-30 Fedor Brunner <fedor.brunner@azet.sk>
+
+ * OpenSSL/SSL.py: Add ``get_finished``, ``get_peer_finished``
+ methods to ``Connection``. If you use these methods to
+ implement TLS channel binding (RFC 5929) disable session
+ resumption because triple handshake attacks against TLS.
+ <https://www.ietf.org/mail-archive/web/tls/current/msg11337.html>
+ <https://secure-resumption.com/tlsauth.pdf>
+
+2014-03-29 Fedor Brunner <fedor.brunner@azet.sk>
+
+ * OpenSSL/SSL.py: Add ``get_cipher_name``, ``get_cipher_bits``,
+ and ``get_cipher_version`` to ``Connection``.
+
+2014-03-28 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/tsafe.py: Replace the use of ``apply`` (which has been
+ removed in Python 3) with the equivalent syntax.
+
+2014-03-28 Jonathan Giannuzzi <jonathan@giannuzzi.be>
+
+ * OpenSSL/crypto.py: Fix memory leak in _X509_REVOKED_dup.
+ * leakcheck/crypto.py: Add checks for _X509_REVOKED_dup, CRL.add_revoked
+ and CRL.get_revoked.
+ * setup.py: Require cryptography 0.3 to have the ASN1_TIME_free binding.
+
+2014-03-02 Stephen Holsapple <sholsapp@gmail.com>
+
+ * OpenSSL/crypto.py: Add ``get_extensions`` method to ``X509Req``.
+
+2014-02-23 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * Release 0.14
+
+2014-01-09 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL: Port to the cffi-based OpenSSL bindings provided by
+ <https://github.com/pyca/cryptography>
+
+2013-10-06 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/ssl/context.c: Add support for negotiating TLS v1.1 or
+ v1.2.
+
+2013-10-03 Christian Heimes <christian@python.org>
+
+ * 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 <christian@python.org>
+
+ * 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 <exarkun@twistedmatrix.com>
+
+ * 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 <exarkun@twistedmatrix.com>
+
+ * 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 <exarkun@twistedmatrix.com>
+
+ * 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 <lp:~dataway>.
+
+2011-09-14 Žiga Seilnacht <lp:ziga-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 <exarkun@twistedmatrix.com>
+
+ * OpenSSL/crypto/crl.c: Add error handling for the use of
+ X509_CRL_sign.
+
+2011-09-11 Jonathan Ballet <lp:multani>
+
+ * doc/: Convert the LaTeX documentation to Sphinx-using ReST.
+ * OpenSSL/: Convert the epytext API documentation to Sphinx-using ReST.
+
+2011-09-08 Guillermo Gonzalez <guillermo.gonzalez@canonical.com>
+
+ * 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 <exarkun@twistedmatrix.com>
+
+ * Release 0.13
+
+2011-06-12 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/crypto/pkey.c: Add the PKey.check method, mostly
+ implemented by Rick Dean, to verify the internal consistency of a
+ PKey instance.
+
+2011-06-12 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/crypto/crypto.c: Fix the sign and verify functions so
+ they handle data with embedded NULs. Fix by David Brodsky
+ <lp:~lihalla>.
+
+2011-05-20 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/ssl/connection.c, OpenSSL/test/test_ssl.py: Add a new
+ method to the Connection type, get_peer_cert_chain, for retrieving
+ the peer's certificate chain.
+
+2011-05-19 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/crypto/x509.c, OpenSSL/test/test_crypto.py: Add a new
+ method to the X509 type, get_signature_algorithm, for inspecting
+ the signature algorithm field of the certificate. Based on a
+ patch from <lp:~okuda>.
+
+2011-05-10 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/crypto/crypto.h: Work around a Windows/OpenSSL 1.0 issue
+ explicitly including a Windows header before any OpenSSL headers.
+
+ * OpenSSL/crypto/pkcs12.c: Work around an OpenSSL 1.0 issue by
+ explicitly flushing errors known to be uninteresting after calling
+ PKCS12_parse.
+
+ * OpenSSL/ssl/context.c: Remove SSLv2 support if the underlying
+ OpenSSL library does not provide it.
+
+ * OpenSSL/test/test_crypto.py: Support an OpenSSL 1.0 change from
+ MD5 to SHA1 by allowing either hash algorithm's result as the
+ return value of X509.subject_name_hash.
+
+ * OpenSSL/test/test_ssl.py: Support an OpenSSL 1.0 change from MD5
+ to SHA1 by constructing certificate files named using both hash
+ algorithms' results when testing Context.load_verify_locations.
+
+ * Support OpenSSL 1.0.0a.
+
+2011-04-15 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/ssl/ssl.c: Add OPENSSL_VERSION_NUMBER, SSLeay_version
+ and related constants for retrieving version information about the
+ underlying OpenSSL library.
+
+2011-04-07 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * Release 0.12
+
+2011-04-06 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/crypto/x509.c: Add get_extension_count and get_extension
+ to the X509 type, allowing read access to certificate extensions.
+
+ * OpenSSL/crypto/x509ext.c: Add get_short_name and get_data to the
+ X509Extension type, allowing read access to the contents of an
+ extension.
+
+2011-03-21 Olivier Hervieu <lp:~ohe>
+
+ * OpenSSL/ssl/ssl.c: Expose a number of symbolic constants for
+ values passed to the connection "info" callback.
+
+2011-01-22 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/ssl/connection.py: Add support for new-style
+ buffers (primarily memoryviews) to Connection.send and
+ Connection.sendall.
+
+2010-11-01 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * Release 0.11
+
+2010-10-07 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * Initial support for Python 3.x throughout the codebase.
+
+2010-09-14 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * OpenSSL/crypto/netscape_spki.c: Fix an off-by-one mistake in the
+ error handling for NetscapeSPKI.verify. Add additional error
+ checking to NetscapeSPKI.sign to handle the case where there is no
+ private key.
+
+ * OpenSSL/crypto/x509.c: Fix an overflow bug in the subject_name_hash
+ method of the X509 type which would cause it to return negative
+ values on 32 bit systems.
+
+ * OpenSSL/crypto/x509req.c: Fix an off-by-one mistake in the error
+ handling for X509Req.verify.
+
+ * OpenSSL/ssl/context.c: Fix the error handling in the load_tmp_dh
+ method of the Context type which would cause it to always raise
+ MemoryError, regardless of the actual error (such as a bad file
+ name).
+
+ * OpenSSL/test/: Numerous unit tests added, both for above fixes
+ and for other previously untested code paths.
+
+2010-07-27 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * Re-arrange the repository so that the package can be built and
+ used in-place without requiring installation.
+
+2010-02-27 James Yonan <james@openvpn.net>
+
+ * src/crypto/crypto.c: Added crypto.sign and crypto.verify methods
+ that wrap EVP_Sign and EVP_Verify function families, using code
+ derived from Dave Cridland's PyOpenSSL branch.
+
+ * test/test_crypto.py: Added unit tests for crypto.sign and
+ crypto.verify.
+
+2010-01-27 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/ssl/connection.c, src/util.h: Apply patch from Sandro Tosi to
+ fix misspellings of "compatibility".
+
+2009-11-13 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * Release 0.10
+
+2009-11-07 Žiga Seilnacht, Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/ssl/connection.c, src/ssl/context.c: Add set_client_ca_list,
+ add_client_ca, and get_client_ca_list to Context for manipulating
+ the list of certificate authority names which are sent by servers
+ with the certificate request message.
+ * src/util.h: Add ssize-related defines if the version of Python
+ being used does not have them.
+ * setup.py: Significant changes to the way Windows builds are done,
+ particularly the way OpenSSL headers and libraries are found (with
+ the new --with-openssl argument to build_ext).
+
+2009-08-27 Rick Dean <rick@fdd.com>, Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/pkcs12.c: Add setters to the PKCS12 type for the
+ certificate, private key, ca certificate list, and friendly
+ name, and add a getter for the friendly name. Also add a method
+ for exporting a PKCS12 object as a string.
+ * test/test_crypto.py: Add lots of additional tests for the PKCS12
+ type.
+ * doc/pyOpenSSL.tex: Documentation for the new PKCS12 methods.
+
+2009-07-17 Rick Dean <rick@fdd.com>, Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/x509ext.c: Add subject and issuer parameters to
+ X509Extension, allowing creation of extensions which require that
+ information. Fixes LP#322813.
+
+2009-07-16 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * test/util.py: Changed the base TestCase's tearDown to assert that
+ no errors were left in the OpenSSL error queue by the test.
+ * src/crypto/crypto.c: Add a private helper in support of the
+ TestCase.tearDown change.
+ * src/crypto/x509name.c: Changed X509Name's getattr implementation
+ to clean up the error queue. Fixes LP#314814.
+ * test/util.c: Changed flush_error_queue to avoid a reference
+ counting bug caused by macro expansion.
+
+2009-07-16 Rick Dean <rick@fdd.com>
+
+ * src/rand.c: Added OpenSSL.rand.bytes to get random bytes directly.
+ * src/util.c: Added generic exceptions_from_error_queue to replace
+ the various other implementations of this function. Also updated
+ the rest of the codebase to use this version instead.
+
+2009-07-05 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * test/util.py, test/test_ssl.py, test/test_crypto.py: Fold the
+ Python 2.3 compatibility TestCase mixin into the TestCase defined
+ in util.py.
+
+2009-07-05 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * test/util.py, test/test_ssl.py, test/test_crypto.py: Stop trying
+ to use Twisted's TestCase even when it's available. Instead,
+ always use the stdlib TestCase with a few enhancements.
+
+2009-07-04 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * Changed most extension types so that they can be instantiated
+ using the type object rather than a factory function. The old
+ factory functions are now aliases for the type objects.
+ Fixes LP#312786.
+
+2009-05-27 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * Changed all docstrings in extension modules to be friendlier
+ towards Python programmers. Fixes LP#312787.
+
+2009-05-27 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/x509ext.c: Correctly deallocate the new Extension
+ instance when there is an error initializing it and it is not
+ going to be returned. Resolves LP#368043.
+
+2009-05-11 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * test/test_crypto.py: Use binary mode for the pipe to talk to the
+ external openssl binary. The data being transported over this
+ pipe is indeed binary, so previously it would often be truncated
+ or otherwise mangled.
+
+ * src/ssl/connection.h, src/ssl/connection.c, test/test_ssl.py:
+ Extend the Connection class with support for in-memory BIOs. This
+ allows SSL to be run without a real socket, useful for
+ implementing EAP-TLS or using SSL with Windows IO completion
+ ports, for example. Based heavily on contributions from Rick
+ Dean.
+
+2009-04-25 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * Release 0.9
+
+2009-04-01 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+ Samuele Pedroni <pedronis@openend.se>
+
+ * src/util.h: Delete the TLS key before trying to set a new value
+ for it in case the current thread identifier is a recycled one (if
+ it is recycled, the key won't be set because there is already a
+ value from the previous thread to have this identifier and to use
+ the pyOpenSSL API).
+
+2009-04-01 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/crypto.c: Add FILETYPE_TEXT for dumping keys and
+ certificates and certificate signature requests to a text format.
+
+2008-12-31 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/x509ext.c, test/test_crypto.py: Add the get_short_name
+ method to X509Extension based on patch from Alex Stapleton.
+
+2008-12-31 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/x509ext.c, test/test_crypto.py: Fix X509Extension so
+ that it is possible to instantiate extensions which use s2i or r2i
+ instead of v2i (an extremely obscure extension implementation
+ detail).
+
+2008-12-30 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * MANIFEST.in, src/crypto/crypto.c, src/crypto/x509.c,
+ src/crypto/x509name.c, src/rand/rand.c, src/ssl/context.c: Changes
+ which eliminate compiler warnings but should not change any
+ behavior.
+
+2008-12-28 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * test/test_ssl.py, src/ssl/ssl.c: Expose DTLS-related constants,
+ OP_NO_QUERY_MTU, OP_COOKIE_EXCHANGE, and OP_NO_TICKET.
+
+2008-12-28 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/ssl/context.c: Add a capath parameter to
+ Context.load_verify_locations to allow Python code to specify
+ either or both arguments to the underlying
+ SSL_CTX_load_verify_locations API.
+ * src/ssl/context.c: Add Context.set_default_verify_paths, a wrapper
+ around SSL_CTX_set_default_verify_paths.
+
+2008-12-28 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * test/test_crypto.py, src/crypto/x509req.c: Added get_version and
+ set_version_methods to X509ReqType based on patch from Wouter van
+ Bommel. Resolves LP#274418.
+
+2008-09-22 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * Release 0.8
+
+2008-10-19 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * tsafe.py: Revert the deprecation of the thread-safe Connection
+ wrapper. The Connection class should not segfault if used from
+ multiple threads now, but it generally cannot be relied on to
+ produce correct results if used without the thread-safe wrapper.
+ * doc/pyOpenSSL.tex: Correct the documentation for the set_passwd_cb
+ callback parameter so that it accurately describes the required
+ signature.
+
+2008-09-22 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * Release 0.8a1
+
+2008-09-21 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/ssl/ssl.h, src/ssl/ssl.c: Add a thread-local storage key
+ which will be used to store and retrieve PyThreadState pointers
+ whenever it is necessary to release or re-acquire the GIL.
+
+ * src/ssl/context.c: Change global_verify_callback so that it
+ unconditionally manipulates the Python threadstate, rather than
+ checking the tstate field which is now always NULL.
+
+2008-04-26 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/ssl/context.c: Change global_passphrase_callback and
+ global_info_callback so that they acquire the GIL before
+ invoking any CPython APIs and do not release it until after they
+ are finished invoking all of them (based heavily on on patch
+ from Dan Williams).
+ * src/ssl/crypto.c: Initialize OpenSSL thread support so that it
+ is valid to use OpenSSL APIs from more than one thread (based on
+ patch from Dan Williams).
+ * test/test_crypto.py: Add tests for load_privatekey and
+ dump_privatekey when a passphrase or a passphrase callback is
+ supplied.
+ * test/test_ssl.py: Add tests for Context.set_passwd_cb and
+ Context.set_info_callback.
+
+2008-04-11 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * Release 0.7
+
+2008-03-26 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/x509name.c: Add X509Name.get_components
+
+2008-03-25 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/x509name.c: Add hash and der methods to X509Name.
+ * src/crypto/x509.c: Fix a bug in X509.get_notBefore and
+ X509.get_notAfter preventing UTCTIME format timestamps from
+ working.
+
+2008-03-12 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * Fix coding problems in examples/. Remove keys and certificates
+ and add a note about how to generate new ones.
+
+2008-03-09 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/x509.c: Add getters and setters for the notBefore and
+ notAfter attributes of X509s.
+ * src/crypto/pkey.h, src/crypto/pkey.c, src/crypto/x509req.c,
+ src/crypto/x509.c: Track the initialized and public/private state
+ of EVP_PKEY structures underlying the crypto_PKeyObj type and
+ reject X509Req signature operations on keys not suitable for the
+ task.
+
+2008-03-06 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/x509name.c: Fix tp_compare so it only returns -1, 0, or
+ 1. This eliminates a RuntimeWarning emitted by Python.
+ * src/crypto/x509req.c: Fix reference counting for X509Name returned
+ by X509Req.get_subject. This removes a segfault when the subject
+ name outlives the request object.
+ * src/crypto/x509.c: Change get_serial_number and set_serial_number
+ to accept Python longs.
+ * doc/pyOpenSSL.tex: A number of minor corrections.
+
+2008-03-03 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/crypto/crypto.c: Expose X509_verify_cert_error_string. (patch
+ from Victor Stinner)
+
+2008-02-22 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/ssl/connection.c src/ssl/context.c src/ssl/ssl.c: Fix
+ compilation on Windows. (patch from Michael Schneider)
+
+2008-02-21 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/ssl/connection.c: Expose SSL_get_shutdown and
+ SSL_set_shutdown. (patch from James Knight)
+ * src/ssl/ssl.c: Expose SSL_SENT_SHUTDOWN and SSL_RECEIVED_SHUTDOWN.
+ (patch from James Knight)
+
+2008-02-19 Jean-Paul Calderone <exarkun@twistedmatrix.com>
+
+ * src/ssl/context.c: Expose SSL_CTX_add_extra_chain_cert.
+ * src/crypto/x509name.c: Fix memory leaks in __getattr__ and
+ __setattr_ implementations.
+ * src/crypto/x509.c: Fix memory leak in X509.get_pubkey().
+ * leakcheck/: An attempt at a systematic approach to leak
+ elimination.
+
+2004-08-13 Martin Sjögren <msjogren@gmail.com>
+
+ * Released version 0.6.
+
+2004-08-11 Martin Sjögren <msjogren@gmail.com>
+
+ * doc/pyOpenSSL.tex: Updates to the docs.
+
+2004-08-10 Martin Sjögren <msjogren@gmail.com>
+
+ * src/crypto/x509.c: Add X509.add_extensions based on a patch
+ from Han S. Lee.
+ * src/ssl/ssl.c: Add more SSL_OP_ constants. Patch from Mihai
+ Ibanescu.
+
+2004-08-09 Martin Sjögren <msjogren@gmail.com>
+
+ * setup.py src/crypto/: Add support for Netscape SPKI extensions
+ based on a patch from Tollef Fog Heen.
+ * src/crypto/crypto.c: Add support for python passphrase callbacks
+ based on a patch from Robert Olson.
+
+2004-08-03 Martin Sjögren <msjogren@gmail.com>
+
+ * src/ssl/context.c: Applied patch from Frederic Peters to add
+ Context.use_certificate_chain_file.
+ * src/crypto/x509.c: Applid patch from Tollef Fog Heen to add
+ X509.subject_name_hash and X509.digest.
+
+2004-08-02 Martin Sjögren <msjogren@gmail.com>
+
+ * src/crypto/crypto.c src/ssl/ssl.c: Applied patch from Bastian
+ Kleineidam to fix full names of exceptions.
+
+2004-07-19 Martin Sjögren <msjogren@gmail.com>
+
+ * doc/pyOpenSSL.tex: Fix the errors regarding X509Name's field names.
+
+2004-07-18 Martin Sjögren <msjogren@gmail.com>
+
+ * examples/certgen.py: Fixed wrong attributes in doc string, thanks
+ Remy. (SFbug#913315)
+ * __init__.py, setup.py, version.py: Add __version__, as suggested by
+ Ronald Oussoren in SFbug#888729.
+ * examples/proxy.py: Fix typos, thanks Mihai Ibanescu. (SFpatch#895820)
+
+2003-01-09 Martin Sjögren <martin@strakt.com>
+
+ * Use cyclic GC protocol in SSL.Connection, SSL.Context, crypto.PKCS12
+ and crypto.X509Name.
+
+2002-12-02 Martin Sjögren <martin@strakt.com>
+
+ * tsafe.py: Add some missing methods.
+
+2002-10-06 Martin Sjögren <martin@strakt.com>
+
+ * __init__.py: Import tsafe too!
+
+2002-10-05 Martin Sjögren <martin@strakt.com>
+
+ * src/crypto/x509name.c: Use unicode strings instead of ordinary
+ strings in getattr/setattr. Note that plain ascii strings should
+ still work.
+
+2002-09-17 Martin Sjögren <martin@strakt.com>
+
+ * Released version 0.5.1.
+
+2002-09-09 Martin Sjögren <martin@strakt.com>
+
+ * setup.cfg: Fixed build requirements for rpms.
+
+2002-09-07 Martin Sjögren <martin@strakt.com>
+
+ * src/ssl/connection.c: Fix sendall() method. It segfaulted because
+ it was too generous about giving away the GIL.
+ * Added SecureXMLRPCServer example, contributed by Michal Wallace.
+
+2002-09-06 Martin Sjögren <martin@strakt.com>
+
+ * setup.cfg: Updated the build requirements.
+ * src/ssl/connection.c: Fix includes for AIX.
+
+2002-09-04 Anders Hammarquist <iko@strakt.com>
+
+ * Added type checks in all the other places where we expect
+ specific types of objects passed.
+
+2002-09-04 Martin Sjögren <martin@strakt.com>
+
+ * src/crypto/crypto.c: Added an explicit type check in the dump_*
+ functions, so that they won't die when e.g. None is passed in.
+
+2002-08-25 Martin Sjögren <martin@strakt.com>
+
+ * doc/pyOpenSSL.tex: Docs for PKCS12.
+
+2002-08-24 Martin Sjögren <martin@strakt.com>
+
+ * src/crypto: Added basic PKCS12 support, thanks to Mark Welch
+ <mark@collab.net>
+
+2002-08-16 Martin Sjögren <martin@strakt.com>
+
+ * D'oh! Fixes for python 1.5 and python 2.1.
+
+2002-08-15 Martin Sjögren <martin@strakt.com>
+
+ * Version 0.5. Yay!
+
+2002-07-25 Martin Sjögren <martin@strakt.com>
+
+ * src/ssl/context.c: Added set_options method.
+ * src/ssl/ssl.c: Added constants for Context.set_options method.
+
+2002-07-23 Martin Sjögren <martin@strakt.com>
+
+ * Updated docs
+ * src/ssl/connection.c: Changed the get_cipher_list method to actually
+ return a list! WARNING: This change makes the API incompatible with
+ earlier versions!
+
+2002-07-15 Martin Sjögren <martin@strakt.com>
+
+ * src/ssl/connection.[ch]: Removed the fileno method, it uses the
+ transport object's fileno instead.
+
+2002-07-09 Martin Sjögren <martin@strakt.com>
+
+ * src/crypto/x509.c src/crypto/x509name.c: Fixed segfault bug where
+ you used an X509Name after its X509 had been destroyed.
+ * src/crypto/crypto.[ch] src/crypto/x509req.c src/crypto/x509ext.[ch]:
+ Added X509 Extension support. Thanks to maas-Maarten Zeeman
+ <maas@awanim.com>
+ * src/crypto/pkey.c: Added bits() and type() methods.
+
+2002-07-08 Martin Sjögren <martin@strakt.com>
+
+ * src/ssl/connection.c: Moved the contents of setup_ssl into the
+ constructor, thereby fixing some segfault bugs :)
+ * src/ssl/connection.c: Added connect_ex and sendall methods.
+ * src/crypto/x509name.c: Cleaned up comparisons and NID lookup.
+ Thank you Maas-Maarten Zeeman <maas@awanim.com>
+ * src/rand/rand.c: Fix RAND_screen import.
+ * src/crypto/crypto.c src/crypto/pkcs7.[ch]: Added PKCS7 management,
+ courtesy of Maas-Maarten Zeeman <maas@awanim.com>
+ * src/crypto/x509req.c: Added verify method.
+
+2002-06-17 Martin Sjögren <martin@strakt.com>
+
+ * rpm/, setup.cfg: Added improved RPM-building stuff, thanks to
+ Mihai Ibanescu <misa@redhat.com>
+
+2002-06-14 Martin Sjögren <martin@strakt.com>
+
+ * examples/proxy.py: Example code for using OpenSSL through a proxy
+ contributed by Mihai Ibanescu <misa@redhat.com>
+ * Updated installation instruction and added them to the TeX manual.
+
+2002-06-13 Martin Sjögren <martin@strakt.com>
+
+ * src/ssl/context.c: Changed global_verify_callback so that it uses
+ PyObject_IsTrue instead of requiring ints.
+ * Added pymemcompat.h to make the memory management uniform and
+ backwards-compatible.
+ * src/util.h: Added conditional definition of PyModule_AddObject and
+ PyModule_AddIntConstant
+ * src/ssl/connection.c: Socket methods are no longer explicitly
+ wrapped. fileno() is the only method the transport layer object HAS
+ to support, but if you want to use connect, accept or sock_shutdown,
+ then the transport layer object has to supply connect, accept
+ and shutdown respectively.
+
+2002-06-12 Martin Sjögren <martin@strakt.com>
+
+ * Changed comments to docstrings that are visible in Python.
+ * src/ssl/connection.c: Added set_connect_state and set_accept_state
+ methods. Thanks to Mark Welch <mark@collab.net> for this.
+
+2002-06-11 Martin Sjögren <martin@strakt.com>
+
+ * src/ssl/connection.c: accept and connect now use SSL_set_accept_state
+ and SSL_set_connect_state respectively, instead of SSL_accept and
+ SSL_connect.
+ * src/ssl/connection.c: Added want_read and want_write methods.
+
+2002-06-05 Martin Sjögren <martin@strakt.com>
+
+ * src/ssl/connection.c: Added error messages for windows. The code is
+ copied from Python's socketmodule.c. Ick.
+ * src/ssl/connection.c: Changed the parameters to the SysCallError. It
+ always has a tuple (number, string) now, even though the number
+ might not always be useful.
+
+2002-04-05 Martin Sjögren <md9ms@mdstud.chalmers.se>
+
+ * Worked more on the Debian packaging, hopefully the packages
+ are getting into the main Debian archive soon.
+
+2002-01-10 Martin Sjögren <martin@strakt.com>
+
+ * Worked some more on the Debian packaging, it's turning out real
+ nice.
+ * Changed format on this file, I'm going to try to be a bit more
+ verbose about my changes, and this format makes it easier.
+
+2002-01-08 Martin Sjögren <martin@strakt.com>
+
+ * Version 0.4.1
+ * Added some example code
+ * Added the thread safe Connection object in the 'tsafe' submodule
+ * New Debian packaging
+
+2001-08-09 Martin Sjögren <martin@strakt.com>
+
+ * Version 0.4
+ * Added a compare function for X509Name structures.
+ * Moved the submodules to separate .so files, with tiny C APIs so they
+ can communicate
+ * Skeletal OpenSSL/__init__.py
+ * Removed the err submodule, use crypto.Error and SSL.Error instead
+
+2001-08-06 Martin Sjögren <martin@strakt.com>
+
+ * Version 0.3
+ * Added more types for dealing with certificates (X509Store, X509Req,
+ PKey)
+ * Functionality to load private keys, certificates and certificate
+ requests from memory buffers, and store them too
+ * X509 and X509Name objects can now be modified as well, very neat when
+ creating certificates ;)
+ * Added SSL_MODE_AUTO_RETRY to smooth things for blocking sockets
+ * Added a sock_shutdown() method to the Connection type
+ * I don't understand why, but I can't use Py_InitModule() to create
+ submodules in Python 2.0, the interpreter segfaults on the cleanup
+ process when I do. I added a conditional compile on the version
+ number, falling back to my own routine. It would of course be nice to
+ investigate what is happening, but I don't have the time to do so
+ * Do INCREF on the type objects before inserting them in the
+ dictionary, so they will never reach refcount 0 (they are, after all,
+ statically allocated)
+
+2001-07-30 Martin Sjögren <martin@strakt.com>
+
+ * Version 0.2
+ * Lots of tweaking and comments in the code
+ * Now uses distutils instead of the stupid Setup file
+ * Hacked doc/tools/mkhowto, html generation should now work
+
+2001-07-16 Martin Sjögren <martin@strakt.com>
+
+ * Initial release (0.1, don't expect much from this one :-)
+
-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 <target>' where <target> 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."
--- /dev/null
+< Screwtape> I like how developing against OpenSSL is like a text adventure game with a maze of twisty passages, all alike.
+%
--- /dev/null
+This is the pyOpenSSL documentation source. It uses Sphinx. To build the
+documentation, install Sphinx and run:
+
+ $ make html
--- /dev/null
+.. _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/ssl
--- /dev/null
+.. _openssl-crypto:
+
+:py:mod:`crypto` --- Generic cryptographic module
+=================================================
+
+.. py:module:: OpenSSL.crypto
+ :synopsis: Generic cryptographic module
+
+.. note::
+
+ `pyca/cryptography`_ is likely a better choice than using this module.
+ It contains a complete set of cryptographic primitives as well as a significantly better and more powerful X509 API.
+ If necessary you can convert to and from cryptography objects using the ``to_cryptography`` and ``from_cryptography`` methods on ``X509``, ``X509Req``, ``CRL``, and ``PKey``.
+
+
+Elliptic curves
+---------------
+
+.. autofunction:: get_elliptic_curves
+
+.. autofunction:: get_elliptic_curve
+
+Serialization and deserialization
+---------------------------------
+
+The following serialization functions take one of these constants to determine the format.
+
+.. py:data:: FILETYPE_PEM
+
+:data:`FILETYPE_PEM` serializes data to a Base64-encoded encoded representation of the underlying ASN.1 data structure. This representation includes delimiters that define what data structure is contained within the Base64-encoded block: for example, for a certificate, the delimiters are ``-----BEGIN CERTIFICATE-----`` and ``-----END CERTIFICATE-----``.
+
+.. py:data:: FILETYPE_ASN1
+
+:data:`FILETYPE_ASN1` serializes data to the underlying ASN.1 data structure. The format used by :data:`FILETYPE_ASN1` is also sometimes referred to as DER.
+
+Certificates
+~~~~~~~~~~~~
+
+.. autofunction:: dump_certificate
+
+.. autofunction:: load_certificate
+
+Certificate signing requests
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autofunction:: dump_certificate_request
+
+.. autofunction:: load_certificate_request
+
+Private keys
+~~~~~~~~~~~~
+
+.. autofunction:: dump_privatekey
+
+.. autofunction:: load_privatekey
+
+Public keys
+~~~~~~~~~~~
+
+.. autofunction:: dump_publickey
+
+.. autofunction:: load_publickey
+
+Certificate revocation lists
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autofunction:: dump_crl
+
+.. autofunction:: load_crl
+
+.. autofunction:: load_pkcs7_data
+
+.. autofunction:: load_pkcs12
+
+Signing and verifying signatures
+--------------------------------
+
+.. autofunction:: sign
+
+.. autofunction:: verify
+
+
+.. _openssl-x509:
+
+X509 objects
+------------
+
+.. autoclass:: X509
+ :members:
+
+.. _openssl-x509name:
+
+X509Name objects
+----------------
+
+.. autoclass:: X509Name
+ :members:
+ :special-members:
+ :exclude-members: __repr__, __getattr__, __weakref__
+
+.. _openssl-x509req:
+
+X509Req objects
+---------------
+
+.. autoclass:: X509Req
+ :members:
+ :special-members:
+ :exclude-members: __weakref__
+
+.. _openssl-x509store:
+
+X509Store objects
+-----------------
+
+.. autoclass:: X509Store
+ :members:
+
+.. _openssl-x509storecontexterror:
+
+X509StoreContextError objects
+-----------------------------
+
+.. autoclass:: X509StoreContextError
+ :members:
+
+.. _openssl-x509storecontext:
+
+X509StoreContext objects
+------------------------
+
+.. autoclass:: X509StoreContext
+ :members:
+
+.. _openssl-pkey:
+
+X509StoreFlags constants
+------------------------
+
+.. autoclass:: X509StoreFlags
+
+ .. data:: CRL_CHECK
+ .. data:: CRL_CHECK_ALL
+ .. data:: IGNORE_CRITICAL
+ .. data:: X509_STRICT
+ .. data:: ALLOW_PROXY_CERTS
+ .. data:: POLICY_CHECK
+ .. data:: EXPLICIT_POLICY
+ .. data:: INHIBIT_MAP
+ .. data:: NOTIFY_POLICY
+ .. data:: CHECK_SS_SIGNATURE
+ .. data:: CB_ISSUER_CHECK
+
+.. _openssl-x509storeflags:
+
+PKey objects
+------------
+
+.. autoclass:: PKey
+ :members:
+
+.. _openssl-pkcs7:
+
+.. py:data:: TYPE_RSA
+ TYPE_DSA
+
+ Key type constants.
+
+PKCS7 objects
+-------------
+
+PKCS7 objects have the following methods:
+
+.. autoclass:: PKCS7
+ :members:
+
+.. _openssl-pkcs12:
+
+PKCS12 objects
+--------------
+
+.. autoclass:: PKCS12
+ :members:
+
+.. _openssl-509ext:
+
+X509Extension objects
+---------------------
+
+.. autoclass:: X509Extension
+ :members:
+ :special-members:
+ :exclude-members: __weakref__
+
+.. _openssl-netscape-spki:
+
+NetscapeSPKI objects
+--------------------
+
+.. autoclass:: NetscapeSPKI
+ :members:
+ :special-members:
+ :exclude-members: __weakref__
+
+.. _crl:
+
+CRL objects
+-----------
+
+.. autoclass:: CRL
+ :members:
+ :special-members:
+ :exclude-members: __weakref__
+
+.. _revoked:
+
+Revoked objects
+---------------
+
+.. autoclass:: Revoked
+ :members:
+
+Exceptions
+----------
+
+.. py:exception:: Error
+
+ Generic exception used in the :py:mod:`.crypto` module.
+
+
+Digest names
+------------
+
+Several of the functions and methods in this module take a digest name.
+These must be strings describing a digest algorithm supported by OpenSSL (by ``EVP_get_digestbyname``, specifically).
+For example, :const:`b"sha256"` or :const:`b"sha384"`.
+
+More information and a list of these digest names can be found in the ``EVP_DigestInit(3)`` man page of your OpenSSL installation.
+This page can be found online for the latest version of OpenSSL:
+https://www.openssl.org/docs/manmaster/man3/EVP_DigestInit.html
+
+.. _`pyca/cryptography`: https://cryptography.io
--- /dev/null
+.. _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
+ OP_SINGLE_ECDH_USE
+
+ Constants used with :py:meth:`set_options` of Context objects.
+
+ When these options are used, a new key will always be created when using
+ ephemeral (Elliptic curve) 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:: 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.
+
+
+.. autofunction:: SSLeay_version
+
+
+.. py:data:: ContextType
+
+ See :py:class:`Context`.
+
+
+.. autoclass:: Context
+
+.. autoclass:: Session
+
+
+.. 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:
+
+.. autoclass:: OpenSSL.SSL.Context
+ :members:
+
+.. _openssl-session:
+
+Session objects
+---------------
+
+Session objects have no methods.
+
+
+.. _openssl-connection:
+
+Connection objects
+------------------
+
+Connection objects have the following methods:
+
+.. autoclass:: OpenSSL.SSL.Connection
+ :members:
+
+
+.. 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!
--- /dev/null
+Backward Compatibility
+======================
+
+pyOpenSSL has a very strong backward compatibility policy.
+Generally speaking, you shouldn't ever be afraid of updating.
+
+If breaking changes are needed do be done, they are:
+
+#. …announced in the :doc:`changelog`.
+#. …the old behavior raises a :exc:`DeprecationWarning` for a year.
+#. …are done with another announcement in the :doc:`changelog`.
--- /dev/null
+.. include:: ../CHANGELOG.rst
--- /dev/null
+# -*- 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 datetime
+import codecs
+import os
+import re
+import sys
+
+
+HERE = os.path.abspath(os.path.dirname(__file__))
+
+
+def read_file(*parts):
+ """
+ Build an absolute path from *parts* and return the contents of the
+ resulting file. Assume UTF-8 encoding.
+ """
+ with codecs.open(os.path.join(HERE, *parts), "rb", "ascii") as f:
+ return f.read()
+
+
+def find_version(*file_paths):
+ version_file = read_file(*file_paths)
+ version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
+ version_file, re.M)
+ if version_match:
+ return version_match.group(1)
+ raise RuntimeError("Unable to find version string.")
+
+
+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 = [
+ "sphinx.ext.autodoc",
+ 'sphinx.ext.intersphinx',
+]
+
+# 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'
+authors = u"The pyOpenSSL developers"
+copyright = u"2001 " + authors
+
+# 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 = find_version("..", "src", "OpenSSL", "version.py")
+# The full version, including alpha/beta/rc tags.
+release = version
+
+# 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.
+on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
+
+if not on_rtd: # only import and set the theme if we're building docs locally
+ import sphinx_rtd_theme
+ html_theme = 'sphinx_rtd_theme'
+ html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
+
+# 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
+# "<project> v<release> 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 <link> 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',
+ authors, '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',
+ [authors], 1)
+]
+
+intersphinx_mapping = {
+ "https://docs.python.org/3": None,
+ "https://cryptography.io/en/latest/": None,
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="637.02179"
+ height="374.65021"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="/home/glyph"
+ sodipodi:docname="pyopenssl.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="280.76485"
+ inkscape:cy="162.58007"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1243"
+ inkscape:window-height="997"
+ inkscape:window-x="31"
+ inkscape:window-y="21"
+ inkscape:showpageshadow="true"
+ showborder="false" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-48,-348.42626)">
+ <g
+ id="g3322"
+ transform="translate(-46,0)">
+ <path
+ sodipodi:nodetypes="cccccc"
+ id="rect3192"
+ d="M 659.97827,570.41187 L 731.02176,570.41187 L 694.59319,588.79075 L 731.02176,607.16964 L 659.97827,607.16964 L 659.97827,570.41187 z "
+ style="fill:#e90000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.67079389;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" />
+ <path
+ id="rect3182"
+ d="M 134.03397,541.66071 C 111.9415,541.66071 94,562.76288 94,588.78571 C 94,614.80857 111.9415,635.94196 134.03397,635.94196 C 136.3665,635.94196 138.62869,635.67498 140.85222,635.22321 L 595.87054,635.22321 L 595.87054,542.34821 L 140.85222,542.34821 C 138.62869,541.89644 136.3665,541.66071 134.03397,541.66071 z "
+ style="fill:#00b900;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ transform="translate(15.714286,-0.7142857)"
+ style="fill:#00b900;fill-opacity:1"
+ id="g3222">
+ <path
+ style="opacity:1;fill:#00b900;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:34.88786697;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 528.3125,511.0625 C 518.6717,511.04975 510.84826,518.8592 510.84375,528.5 L 510.84375,650.5 C 510.83762,655.13488 512.6761,659.58169 515.95346,662.85904 C 519.23081,666.1364 523.67762,667.97488 528.3125,667.96875 L 605.5625,667.96875 C 615.2033,667.96424 623.01275,660.1408 623,650.5 L 623,528.5 C 622.99549,518.8714 615.1911,511.06701 605.5625,511.0625 L 528.3125,511.0625 z "
+ id="rect3184"
+ sodipodi:nodetypes="cccscccccc" />
+ <path
+ style="opacity:1;fill:#00b900;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:41.47727203;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 584.03125,551.28125 C 572.56922,551.27611 563.27611,560.56922 563.28125,572.03125 L 563.28125,607 C 563.29337,618.44983 572.58142,627.72388 584.03125,627.71875 L 646.84375,627.71875 C 658.28138,627.70665 667.5504,618.43763 667.5625,607 L 667.5625,572.03125 C 667.56763,560.58142 658.29358,551.29337 646.84375,551.28125 L 584.03125,551.28125 z "
+ id="rect3190" />
+ </g>
+ <g
+ transform="translate(15.714286,-2.846549)"
+ id="g3218">
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path3206"
+ sodipodi:cx="564.28571"
+ sodipodi:cy="551.64789"
+ sodipodi:rx="21.428572"
+ sodipodi:ry="21.428572"
+ d="M 585.71428 551.64789 A 21.428572 21.428572 0 1 1 542.85713,551.64789 A 21.428572 21.428572 0 1 1 585.71428 551.64789 z"
+ transform="translate(2.6361696,0)" />
+ <path
+ d="M 585.71428 551.64789 A 21.428572 21.428572 0 1 1 542.85713,551.64789 A 21.428572 21.428572 0 1 1 585.71428 551.64789 z"
+ sodipodi:ry="21.428572"
+ sodipodi:rx="21.428572"
+ sodipodi:cy="551.64789"
+ sodipodi:cx="564.28571"
+ id="path3208"
+ style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc"
+ transform="translate(2.6361696,80)" />
+ </g>
+ <g
+ transform="translate(15.714286,7.8520886)"
+ id="g3214">
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path3210"
+ sodipodi:cx="564.28571"
+ sodipodi:cy="551.64789"
+ sodipodi:rx="21.428572"
+ sodipodi:ry="21.428572"
+ d="M 585.71428 551.64789 A 21.428572 21.428572 0 1 1 542.85713,551.64789 A 21.428572 21.428572 0 1 1 585.71428 551.64789 z"
+ transform="matrix(0.3666667,0,0,0.3666667,440.95238,363.99605)" />
+ <path
+ d="M 585.71428 551.64789 A 21.428572 21.428572 0 1 1 542.85713,551.64789 A 21.428572 21.428572 0 1 1 585.71428 551.64789 z"
+ sodipodi:ry="21.428572"
+ sodipodi:rx="21.428572"
+ sodipodi:cy="551.64789"
+ sodipodi:cx="564.28571"
+ id="path3212"
+ style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:type="arc"
+ transform="matrix(0.3666667,0,0,0.3666667,440.95238,393.32938)" />
+ </g>
+ </g>
+ <path
+ style="fill:#7f7f7f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:50;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 220.092,348.42626 C 208.29083,348.42743 198.7244,357.99387 198.72322,369.79504 L 198.72322,460.05141 L 229.54768,460.05141 L 229.54768,394.58282 C 229.54853,386.11098 236.40795,379.25157 244.87978,379.25072 L 371.11584,379.25072 C 379.58768,379.25157 386.44709,386.11096 386.44794,394.58282 L 386.44794,460.05141 L 417.29911,460.05141 L 417.29911,369.79504 C 417.29792,357.99386 407.7315,348.42744 395.93033,348.42626 L 220.092,348.42626 z "
+ id="rect2182" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect2160"
+ width="268.57144"
+ height="268.57144"
+ x="173.71428"
+ y="454.50504" />
+ <path
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;opacity:1"
+ d="M 308,488.80638 C 278.42857,488.80638 254.4375,512.8287 254.4375,542.40013 C 254.4375,562.41092 265.41804,579.85847 281.6875,589.05638 L 224.09375,688.77513 L 391.90625,688.77513 L 334.3125,589.05638 C 350.58196,579.85847 361.56251,562.41092 361.5625,542.40013 C 361.5625,512.8287 337.57142,488.80639 308,488.80638 z "
+ id="path3292" />
+ </g>
+</svg>
--- /dev/null
+=====================================
+Welcome to pyOpenSSL's documentation!
+=====================================
+
+Release v\ |release| (:doc:`What's new? <changelog>`).
+
+pyOpenSSL is a rather thin wrapper around (a subset of) the OpenSSL library.
+With thin wrapper we mean that a lot of the object methods do nothing more than
+calling a corresponding function in the OpenSSL library.
+
+
+Contents:
+=========
+
+.. toctree::
+ :maxdepth: 2
+
+ introduction
+ install
+ api
+ internals
+
+There are also `examples in the pyOpenSSL repository <https://github.com/pyca/pyopenssl/tree/master/examples>`_ that may help you getting started.
+
+
+Meta
+----
+
+.. toctree::
+ :maxdepth: 1
+
+ backward-compatibility
+ changelog
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
--- /dev/null
+.. include:: ../INSTALL.rst
--- /dev/null
+.. _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?
--- /dev/null
+.. _intro:
+
+============
+Introduction
+============
+
+
+History
+=======
+
+pyOpenSSL was originally created by Martin Sjögren because the SSL support in the standard library 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.
+
+Later it was maintained by `Jean-Paul Calderone`_ who among other things managed to make pyOpenSSL a pure Python project which the current maintainers are *very* grateful for.
+
+Over the time the standard library's ``ssl`` module improved, never reaching the completeness of pyOpenSSL's API coverage.
+Despite `PEP 466`_ many useful features remain Python 3-only and pyOpenSSL remains the only alternative for full-featured TLS code across all noteworthy Python versions from 2.7 through 3.5 and PyPy_.
+
+
+Development
+===========
+
+pyOpenSSL is collaboratively developed by the Python Cryptography Authority (PyCA_) that also maintains the low-level bindings called cryptography_.
+
+Current maintainer and release manager is `Hynek Schlawack`_.
+
+
+.. include:: ../CONTRIBUTING.rst
+
+
+.. _Jean-Paul Calderone: https://github.com/exarkun
+.. _PyPy: http://pypy.org
+.. _PEP 466: https://www.python.org/dev/peps/pep-0466/
+.. _PyCA: https://github.com/pyca
+.. _cryptography: https://github.com/pyca/cryptography
+.. _Hynek Schlawack: https://hynek.me/
--- /dev/null
+@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 ^<target^>` where ^<target^> 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
+++ /dev/null
-\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}
+++ /dev/null
-#! /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()
+++ /dev/null
-#! /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 "<Node for %s (%s)>" % (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"""(.*)<tt(?: class=['"][a-z0-9]+["'])?>(.*)</tt>(.*)$""",
- 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 = ["<dl compact>"]
- 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("<dl compact>" * (count - level) + "\n")
- level = count
- elif level > count:
- append("\n")
- append(level * DL_LEVEL_INDENT)
- append("</dl>" * (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<dt>%s%s\n<dd>\n%s<dl compact>"
- % (term, extra, level * DL_LEVEL_INDENT))
- append("\n%s<dt>%s%s</a>"
- % (level * DL_LEVEL_INDENT, node.links[0], node.text[-1]))
- for link in node.links[1:]:
- append(",\n%s %s[Link]</a>" % (level * DL_LEVEL_INDENT, link))
- previous = current
- append("\n")
- append("</dl>" * (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('<table width="100%"><tr valign="top">')
- for col in split_columns(nodes, columns):
- append('<td width="%d%%">\n' % colwidth)
- append(format_column(col))
- append("\n</td>")
- append("\n</tr></table>")
- else:
- append(format_column(nodes))
- append("\n<p>\n")
- return string.join(strings, '')
-
-
-def format_letter(letter):
- if letter == '.':
- lettername = ". (dot)"
- elif letter == '_':
- lettername = "_ (underscore)"
- else:
- lettername = string.upper(letter)
- return "\n<hr>\n<h2><a name=\"letter-%s\">%s</a></h2>\n\n" \
- % (letter, lettername)
-
-
-def format_html_letters(nodes, columns=1):
- letter_groups = split_letters(nodes)
- items = []
- for letter, nodes in letter_groups:
- s = "<b><a href=\"#letter-%s\">%s</a></b>" % (letter, letter)
- items.append(s)
- s = ["<hr><center>\n%s</center>\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()
+++ /dev/null
-#!/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 <F<mernst@cs.washington.edu>>
-
-=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__
+++ /dev/null
-#! /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 $?
+++ /dev/null
-# 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}"""
+++ /dev/null
-"""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 "<RepositoryInfo for %s>" % `self.get_cvsroot()`
+++ /dev/null
-#!/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()
+++ /dev/null
-#! /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()
+++ /dev/null
-#!/bin/sh
-sed -e 's/{\\ptt[ ]*\\char[ ]*'"'"'137}/_/g' <"$1" > "@$1" && mv "@$1" $1
+++ /dev/null
-#! /bin/sed -f
-s/{\\tt \\hackscore {}\\hackscore {}/\\sectcode{__/
-s/\\hackscore {}\\hackscore {}/__/
+++ /dev/null
-(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)
+++ /dev/null
-#! /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()
+++ /dev/null
-<p> This document was generated using the <a
- href="http://saftsack.fs.uni-bayreuth.de/;SPMtilde;latex2ht/">
- <strong>LaTeX</strong>2<tt>HTML</tt></a> translator.
-</p>
-
-<p> <a
- href="http://saftsack.fs.uni-bayreuth.de/;SPMtilde;latex2ht/">
- <strong>LaTeX</strong>2<tt>HTML</tt></a> is Copyright ©
- 1993, 1994, 1995, 1996, 1997, <a
- href="http://cbl.leeds.ac.uk/nikos/personal.html">Nikos
- Drakos</a>, Computer Based Learning Unit, University of
- Leeds, and Copyright © 1997, 1998, <a
- href="http://www.maths.mq.edu.au/;SPMtilde;ross/">Ross
- Moore</a>, Mathematics Department, Macquarie University,
- Sydney.
-</p>
-
-<p> The application of <a
- href="http://saftsack.fs.uni-bayreuth.de/;SPMtilde;latex2ht/">
- <strong>LaTeX</strong>2<tt>HTML</tt></a> to the Python
- documentation has been heavily tailored by Fred L. Drake,
- Jr. Original navigation icons were contributed by Christopher
- Petrilli.
-</p>
+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<html>
- <head>
- <title>About the Python Documentation</title>
- <meta name="description"
- content="Overview information about the Python documentation">
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <link rel="STYLESHEET" href="lib/lib.css">
- </head>
- <body>
- <div class="navigation">
- <table width="100%" cellpadding="0" cellspacing="2">
- <tr>
- <td><img width="32" height="32" align="bottom" border="0" alt=""
- src="icons/blank.gif"></td>
- <td><a href="./"
- title="Python Documentation Index"><img width="32" height="32"
- align="bottom" border="0" alt="up"
- src="icons/up.gif"></a></td>
- <td><img width="32" height="32" align="bottom" border="0" alt=""
- src="icons/blank.gif"></td>
- <td align="center" width="100%">About the Python Documentation</td>
- <td><img width="32" height="32" align="bottom" border="0" alt=""
- src="icons/blank.gif"></td>
- <td><img width="32" height="32" align="bottom" border="0" alt=""
- src="icons/blank.gif"></td>
- <td><img width="32" height="32" align="bottom" border="0" alt=""
- src="icons/blank.gif"></td>
- </tr>
- </table>
- <b class="navlabel">Up:</b>
- <span class="sectref">
- <a href="./" title="Python Documentation Index">
- Python Documentation Index</A></span>
- <br>
- </div>
- <hr>
-
- <h2>About the Python Documentation</h2>
-
- <p>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 <a
- href="http://www.python.org/">Python Web site</a>.
-
- <p>A <a href="acks.html">list of contributors</a> is available.
-
- <h2>Comments and Questions</h2>
-
- <p> General comments and questions regarding this document should
- be sent by email to <a href="mailto:python-docs@python.org"
- >python-docs@python.org</a>. If you find specific errors in
- this document, please report the bug at the <a
- href="http://sourceforge.net/bugs/?group_id=5470">Python Bug
- Tracker</a> at <a href="http://sourceforge.net/">SourceForge</a>.
- </p>
-
- <p> Questions regarding how to use the information in this
- document should be sent to the Python news group, <a
- href="news:comp.lang.python">comp.lang.python</a>, or the <a
- href="http://www.python.org/mailman/listinfo/python-list"
- >Python mailing list</a> (which is gated to the newsgroup and
- carries the same content).
- </p>
-
- <p> For any of these channels, please be sure not to send HTML email.
- Thanks.
- </p>
-
- <hr>
- </body>
-</html>
+++ /dev/null
-<html>
- <head>
- <title>Python @RELEASE@ Documentation - @DATE@</title>
- <link rel="STYLESHEET" href="lib/lib.css" type="text/css">
- <meta name="description"
- content="Top-level index to the standard documentation for
- Python @RELEASE@.">
- <style type="text/css">
- a.title { font-weight: bold; font-size: 110%; }
- ul { margin-left: 1em; padding: 0pt; border: 0pt; }
- </style>
- </head>
- <body>
- <div class="navigation">
- <table align="center" width="100%" cellpadding="0" cellspacing="2">
- <tr>
- <td><img width="32" height="32" align="bottom" border="0" alt=""
- src="icons/blank.gif"></td>
- <td><img width="32" height="32" align="bottom" border="0" alt=""
- src="icons/blank.gif"></td>
- <td><img width="32" height="32" align="bottom" border="0" alt=""
- src="icons/blank.gif"></td>
- <td align="center" width="100%">
- <b class="title">Python Documentation</b></td>
- <td><img width="32" height="32" align="bottom" border="0" alt=""
- src="icons/blank.gif"></td>
- <td><a href="modindex.html"><img width="32" height="32"
- align="bottom" border="0" alt="Module Index"
- src="icons/modules.gif"></a></td>
- <td><img width="32" height="32" align="bottom" border="0" alt=""
- src="icons/blank.gif"></A></td>
- </tr>
- </table>
- <hr>
- </div>
- <div align="center" class="titlepage">
- <h1>Python Documentation</h1>
-
- <p>
- <strong>Release @RELEASE@</strong>
- <br>
- <strong>@DATE@</strong>
- </p>
- </div>
-
- <table align="center">
- <tbody>
- <tr><td>
- <ul>
- <li> <a href="tut/tut.html" class="title">Tutorial</a>
- <br>(start here)
-
- <li> <a href="modindex.html" class="title">Global Module Index</a>
- <br>(for quick access to all documentation)
-
- <li> <a href="lib/lib.html" class="title">Library Reference</a>
- <br>(keep this under your pillow)
-
- <li> <a href="mac/mac.html" class="title">Macintosh Module
- Reference</a>
- <br>(this too, if you use a Macintosh)
-
- <li> <a href="inst/inst.html" class="title">Installing
- Python Modules</a>
- <br>(for administrators)
- </ul>
- </td>
- <td>
- <ul>
- <li> <a href="ref/ref.html" class="title">Language Reference</a>
- <br>(for language lawyers)
-
- <li> <a href="ext/ext.html" class="title">Extending and
- Embedding</a>
- <br>(tutorial for C/C++ programmers)
-
- <li> <a href="api/api.html" class="title">Python/C API</a>
- <br>(reference for C/C++ programmers)
-
- <li> <a href="doc/doc.html" class="title">Documenting Python</a>
- <br>(information for documentation authors)
-
- <li> <a href="dist/dist.html" class="title">Distributing
- Python Modules</a>
- <br>(for developers and packagers)
- </ul>
- </td>
- </tr>
- <tr>
- <td>
-
- <ul>
- <li> <a href="http://www.python.org/doc/" class="title"
- >Documentation Central</a>
- <br>(for everyone)
- </ul>
- </td>
- <td>
-
- <ul>
- <li> <a href="http://www.python.org/doc/howto/" class="title"
- >Python How-To Guides</a>
- <br>(special topics)
- </ul>
- </td>
- </tr>
- </tbody>
- </table>
- <p>
-
- <address>
- <hr>
- See <i><a href="about.html">About the Python Documentation</a></i>
- for information on suggesting changes.
- </address>
- </body>
-</html>
+++ /dev/null
-<p> This document was generated using the <a
- href="http://saftsack.fs.uni-bayreuth.de/;SPMtilde;latex2ht/">
- <strong>LaTeX</strong>2<tt>HTML</tt></a> translator.
-</p>
-
-<p> <a
- href="http://saftsack.fs.uni-bayreuth.de/;SPMtilde;latex2ht/">
- <strong>LaTeX</strong>2<tt>HTML</tt></a> is Copyright ©
- 1993, 1994, 1995, 1996, 1997, <a
- href="http://cbl.leeds.ac.uk/nikos/personal.html">Nikos
- Drakos</a>, Computer Based Learning Unit, University of
- Leeds, and Copyright © 1997, 1998, <a
- href="http://www.maths.mq.edu.au/;SPMtilde;ross/">Ross
- Moore</a>, Mathematics Department, Macquarie University,
- Sydney.
-</p>
-
-<p> The application of <a
- href="http://saftsack.fs.uni-bayreuth.de/;SPMtilde;latex2ht/">
- <strong>LaTeX</strong>2<tt>HTML</tt></a> to the Python
- documentation has been heavily tailored by Fred L. Drake,
- Jr. Original navigation icons were contributed by Christopher
- Petrilli.
-</p>
-
-<hr>
-
-<h2>Comments and Questions</h2>
-
-<p> General comments and questions regarding this document should
- be sent by email to <a href="mailto:python-docs@python.org"
- >python-docs@python.org</a>. If you find specific errors in
- this document, please report the bug at the <a
- href="http://sourceforge.net/bugs/?group_id=5470">Python Bug
- Tracker</a> at <a href="http://sourceforge.net/">SourceForge</a>.
-</p>
-
-<p> Questions regarding how to use the information in this
- document should be sent to the Python news group, <a
- href="news:comp.lang.python">comp.lang.python</a>, or the <a
- href="http://www.python.org/mailman/listinfo/python-list"
- >Python mailing list</a> (which is gated to the newsgroup and
- carries the same content).
-</p>
-
-<p> For any of these channels, please be sure not to send HTML email.
- Thanks.
-</p>
+++ /dev/null
-/*
- * 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 }
+++ /dev/null
-#! /usr/bin/env perl
-# html2texi.pl -- Convert HTML documentation to Texinfo format
-# Michael Ernst <mernst@cs.washington.edu>
-# 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 <DT><DL COMPACT><DD> should be <DT><DL COMPACT><DD>.
-
-# 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(@current_contents_list); $i++)
- { my $ref_c_tdf = $current_contents_list[$i];
- if ($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<scalar(@sorted_depths); $i++)
- { $depths{$sorted_depths[$i]} = $i + $current_depth+1; }
- for my $ref_tdf (@current_contents_list)
- { $ {$ref_tdf}[1] = $depths{$ {$ref_tdf}[1]}; }
-
- # Eliminate uninteresting sections. Hard-coded hack for now.
- if ($ {$current_contents_list[-1]}[0] eq "About this document ...")
- { pop @current_contents_list; }
- if ((scalar(@current_contents_list) > 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; $i<scalar(@current_contents_list); $i++)
- { my $ref_tdf = $current_contents_list[$i];
- my $title = $ {$ref_tdf}[0];
- if (exists $index_info{$title})
- { my $index_file = $ {$ref_tdf}[2];
- my ($indexing_command, $suffix) = @{$index_info{$title}};
- process_index_file($index_file, $indexing_command);
- print TEXI "\n\@defindex $suffix\n";
- push @index_titles, $title;
- splice @current_contents_list, $i, 1;
- $i--; }
- elsif ($title =~ /\bIndex$/)
- { print STDERR "Warning: \"$title\" might be an index; if so, edit \%index_info.\n"; } }
-
- merge_contents_lists();
-
- # print_contents_list();
- # print_index_info();
-}
-
-
-sub increment_current_contents_list ( $$$ )
-{ my ($he, $startflag, $depth) = check_args(3, @_);
- if (!$startflag)
- { return; }
-
- if ($he->tag eq "li")
- { my @li_content = @{$he->content};
- if ($li_content[0]->tag ne "a")
- { die "first element of <LI> should be <A>"; }
- 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 <DL COMPACT> list from a LaTeX2HTML index:
-# * a single space: text to be ignored
-# * <DT> elements with an optional <DD> element following each one
-# Two types of <DT> elements:
-# * Followed by a <DD> element: the <DT> contains a single
-# string, and the <DD> contains a whitespace string to be ignored, a
-# <DL COMPACT> to be recursively processed (with the <DT> string as a
-# prefix), and a whitespace string to be ignored.
-# * Not followed by a <DD> element: contains a list of anchors
-# and texts (ignore the texts, which are only whitespace and commas).
-# Optionally contains a <DL COMPACT> to be recursively processed (with
-# the <DT> 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 <DT> 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 <DT> element. If it contains more than one anchor, then
-# the texts of all subsequent ones are "[Link]". Example:
-# <DT>
-# <A HREF="embedding.html#l2h-201">
-# "$PATH"
-# ", "
-# <A HREF="embedding.html#l2h-205">
-# "[Link]"
-# Optionally contains a <DL COMPACT> as well. Example:
-# <DT>
-# <A HREF="types.html#l2h-616">
-# "attribute"
-# <DL COMPACT>
-# <DT>
-# <A HREF="assignment.html#l2h-3074">
-# "assignment"
-# ", "
-# <A HREF="assignment.html#l2h-3099">
-# "[Link]"
-# <DT>
-# <A HREF="types.html#l2h-">
-# "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 <DT> 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 <DT>"; }
-
- my ($aname, $ahref, @acontent) = anchor_info($a);
- # unused $aname
- if (scalar(@acontent) != 1)
- { die "Expected just one content of <A> in <DT>: @acontent"; }
- if (ref $acontent[0])
- { $acontent[0]->dump;
- die "Expected string content of <A> in <DT>: $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 <DT>"; }
- 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 <DT>: @dtcontent"; }
- $dtcontent = $dtcontent[0];
- $dtcontent =~ s/ +$//; }
- my $ddcontent;
- { my @ddcontent = @{$dd->content()};
- if (scalar(@ddcontent) != 1)
- { die "Expected single <DD> content, got ", scalar(@ddcontent), " elements:\n", join("\n", @ddcontent), "\n "; }
- $ddcontent = $ddcontent[0]; }
- if ($ddcontent->tag ne "dl")
- { die "Expected <DL> as content of <DD>, 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 <HTML> at top level"; }
- my @content = @{$he->content()};
- if ((!ref $content[0]) or ($content[0]->tag ne "head"))
- { $he->dump;
- die "<HEAD> not first element of <HTML>"; }
- if ((!ref $content[1]) or ($content[1]->tag ne "body"))
- { $he->dump;
- die "<BODY> not second element of <HTML>"; }
-
- $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 <tt>...</tt>.
- 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 <H1>, 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 "<dl><dd><pre> ... </pre></dl>" 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 <PRE>: ", @{$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 <TR> 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; $i<scalar(@pcontent); $i++)
- { if ($pcontent[$i] eq $he)
- { splice @{$ref_pcontent}, $i, 1;
- $he->parent(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; $i<scalar(@pcontent); $i++)
- { if ($pcontent[$i] eq $orig)
- { $ {$ref_pcontent}[$i] = $new;
- $new->parent($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; $i<scalar(@{$ref_pcontent}); $i++)
- { if (${$ref_pcontent}[$i] eq $he)
- { splice(@{$ref_pcontent}, $i, 1);
- last; } }
- $he->delete();
- 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; $i<scalar(@{$ref_content}); $i++)
- { if ($ {$ref_content}[$i] =~ /^ *$/)
- { splice(@{$ref_content}, $i, 1);
- $i--; } }
-}
-
-sub delete_trailing_spaces ( $ )
-{ my ($he) = check_args(1, @_);
- my $ref_content = $he->content();
- if (! defined $ref_content)
- { return; }
- # Could also check for previous element = /^h[1-6]$/.
- for (my $i = 0; $i<scalar(@{$ref_content})-1; $i++)
- { if ($ {$ref_content}[$i] =~ /^ *$/)
- { my $next_elt = $ {$ref_content}[$i+1];
- if ((ref $next_elt) && ($next_elt->tag =~ /^(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
-# <DT>text
-# <DL COMPACT><DD>text
-# which should actually be:
-# <DL COMPACT>
-# <DT>text
-# <DD>text
-# Since a <DL> gets added, this ends up looking like
-# <P>
-# <DL>
-# <DT>
-# text1...
-# <DL COMPACT>
-# <DD>
-# text2...
-# dt_or_dd1...
-# dt_or_dd2...
-# which should become
-# <P>
-# <DL COMPACT>
-# <DT>
-# text1...
-# <DD>
-# 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 <p>\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 <p> and implicit <dl>\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 <p>, implicit <dl>, and <dt>\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 <p>, implicit <dl>, <dt>, and <dl>\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 <p>, implicit <dl>, <dt>, <dl>, and <dd>\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
-# <P>
-# <HR>
-# <UL>
-# then accumulate its links into a contents_list and delete the paragraph.
-sub process_if_child_links ( $$$ )
-{ my ($he, $startflag) = (check_args(3, @_))[0,1]; # ignore depth argument
- if (!$startflag)
- { return; }
-
- if ($he->tag() eq "p")
- { my $ref_content = $he->content();
- if (defined $ref_content)
- { my @content = @{$ref_content};
- if ((scalar(@content) == 2)
- && (ref $content[0]) && $content[0]->tag() eq "hr"
- && (ref $content[1]) && $content[1]->tag() eq "ul")
- { process_child_links($he);
- $he->delete();
- return 0; } } }
- return 1;
-}
-
-
-# If we find
-# <H4>
-# "Footnotes"
-# <DL>
-# <DT>
-# <A NAME="foot560">
-# "...borrow"
-# <A HREF="refcountsInPython.html#tex2html2" NAME="foot560">
-# "1.2"
-# <DD>
-# "The metaphor of ``borrowing'' a reference is not completely correct: the owner still has a copy of the reference. "
-# ...
-# then record the footnote information and delete the section and list.
-
-my $process_if_footnotes_expect_dl_next = 0;
-
-sub process_if_footnotes ( $$$ )
-{ my ($he, $startflag) = (check_args(3, @_))[0,1]; # ignore depth argument
- if (!$startflag)
- { return; }
-
- if (($he->tag() eq "h4")
- && has_single_content_string($he)
- && ($ {$he->content}[0] eq "Footnotes"))
- { html_replace_by_ignore($he);
- $process_if_footnotes_expect_dl_next = 1;
- return 0; }
-
- if ($process_if_footnotes_expect_dl_next && ($he->tag() eq "dl"))
- { my $ref_content = $he->content();
- if (defined $ref_content)
- { $process_if_footnotes_expect_dl_next = 0;
- my @content = @{$ref_content};
- for (my $i=0; $i<$#content; $i+=2)
- { my $he_dt = $content[$i];
- my $he_dd = $content[$i+1];
- if (($he_dt->tag ne "dt") || ($he_dd->tag ne "dd"))
- { $he->dump;
- die "expected <DT> and <DD> at positions $i and ", $i+1; }
- my @dt_content = @{$he_dt->content()};
- if ((scalar(@dt_content) != 2)
- || ($dt_content[0]->tag ne "a")
- || ($dt_content[1]->tag ne "a"))
- { $he_dt->dump;
- die "Expected 2 anchors as content of <DT>"; }
- my ($dt1_name, $dt1_href, $dt1_content) = anchor_info($dt_content[0]);
- my ($dt2_name, $dt2_href, $dt2_content) = anchor_info($dt_content[0]);
- # unused: $dt1_href, $dt1_content, $dt2_href, $dt2_content
- if ($dt1_name ne $dt2_name)
- { $he_dt->dump;
- die "Expected identical names for anchors"; }
- html_replace_by_ignore($he_dd);
- $he_dd->tag("div"); # has no effect
- $footnotes{$dt1_name} = $he_dd; }
- html_replace_by_ignore($he);
- return 0; } }
-
- if ($process_if_footnotes_expect_dl_next)
- { $he->dump;
- die "Expected <DL> for footnotes next"; }
-
- return 1;
-}
-
-
-
-## Merge two adjacent paragraphs containing <DL> items, such as:
-# <P>
-# <DL>
-# <DT>
-# ...
-# <DD>
-# ...
-# <P>
-# <DL>
-# <DT>
-# ...
-# <DD>
-# ...
-
-sub merge_dl ( $$$ )
-{ my ($he, $startflag) = (check_args(3, @_))[0,1]; # ignore depth argument
- if (!$startflag)
- { return; }
-
- my $ref_content = $he->content;
- if (!defined $ref_content)
- { return; }
- my $i = 0;
- while ($i < scalar(@{$ref_content})-1)
- { my $p1 = $ {$ref_content}[$i];
- if ((ref $p1) && ($p1->tag eq "p")
- && has_single_content_with_tag($p1, "dl"))
- { my $dl1 = $ {$p1->content}[0];
- # In this loop, rhs, not lhs, of < comparison changes,
- # because we are removing elements from the content of $he.
- while ($i < scalar(@{$ref_content})-1)
- { my $p2 = $ {$ref_content}[$i+1];
- if (!((ref $p2) && ($p2->tag eq "p")
- && has_single_content_with_tag($p2, "dl")))
- { last; }
- # Merge these two elements.
- splice(@{$ref_content}, $i+1, 1); # remove $p2
- my $dl2 = $ {$p2->content}[0];
- $dl1->push_content(@{$dl2->content}); # put $dl2's content in $dl1
- }
- # extra increment because next element isn't a candidate for $p1
- $i++; }
- $i++; }
- return 1;
-}
-
-
-
-###########################################################################
-### Testing
-###
-
-sub test ( $$ )
-{ my ($action, $file) = check_args(2, @_);
-
- # General testing
- if (($action eq "view") || ($action eq ""))
- { # # $file = "/homes/gws/mernst/www/links.html";
- # # $file = "/homes/gws/mernst/www/index.html";
- # # $file = "/homes/fish/mernst/java/gud/doc/manual.html";
- # # $file = "/projects/cecil/cecil/doc/manuals/stdlib-man/stdlib/stdlib.html";
- # # $file = "/homes/fish/mernst/tmp/python-doc/html/index.html";
- # $file = "/homes/fish/mernst/tmp/python-doc/html/api/complexObjects.html";
- my $tree = file_to_tree($file);
-
- ## Testing
- # print STDERR $tree->as_HTML;
- $tree->dump();
-
- # print STDERR $tree->tag(), "\n";
- # print STDERR @{$tree->content()}, "\n";
- #
- # for (@{ $tree->extract_links(qw(a img)) }) {
- # my ($link, $linkelem) = @$_;
- # print STDERR "$link ", $linkelem->as_HTML;
- # }
- #
- # print STDERR @{$tree->extract_links()}, "\n";
-
- # my @top_level_elts = @{$tree->content()};
-
- # if scalar(@{$tree->content()})
- return;
- }
-
- elsif ($action eq "raw")
- { my $tree = new HTML::TreeBuilder;
- $tree->ignore_unknown(1);
- # $tree->warn(1);
- $tree->parse_file($file);
-
- $tree->dump();
-
- # cleanup_parse_tree($tree);
- # $tree->dump();
- return;
- }
-
- # Test dealing with a section.
- elsif ($action eq "section")
- { # my $file;
- # $file = "/homes/fish/mernst/tmp/python-doc/html/api/intro.html";
- # $file = "/homes/fish/mernst/tmp/python-doc/html/api/includes.html";
- # $file = "/homes/fish/mernst/tmp/python-doc/html/api/complexObjects.html";
- process_section_file($file, 0, "Title");
- }
-
- # Test dealing with many sections
- elsif (0)
- { my @files = ("/homes/fish/mernst/tmp/python-doc/html/api/about.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/abstract.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/api.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/cObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/complexObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/concrete.html",
- # "/homes/fish/mernst/tmp/python-doc/html/api/contents.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/countingRefs.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/debugging.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/dictObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/embedding.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/exceptionHandling.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/exceptions.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/fileObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/floatObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/front.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/fundamental.html",
- # "/homes/fish/mernst/tmp/python-doc/html/api/genindex.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/importing.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/includes.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/index.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/initialization.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/intObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/intro.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/listObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/longObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/mapObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/mapping.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/newTypes.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/node24.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/noneObject.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/number.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/numericObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/object.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/objects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/os.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/otherObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/processControl.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/refcountDetails.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/refcounts.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/sequence.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/sequenceObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/standardExceptions.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/stringObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/threads.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/tupleObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/typeObjects.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/types.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/utilities.html",
- "/homes/fish/mernst/tmp/python-doc/html/api/veryhigh.html");
- for my $file (@files)
- { print STDERR "\n", "=" x 75, "\n", "$file:\n";
- process_section_file($file, 0, "Title");
- }
- }
-
- # Test dealing with index.
- elsif ($action eq "index")
- { # my $file;
- # $file = "/homes/fish/mernst/tmp/python-doc/html/api/genindex.html";
-
- process_index_file($file, "\@cindex");
- print_index_info();
- }
-
- else
- { die "Unrecognized action `$action'"; }
-}
-
-
-###########################################################################
-### Main loop
-###
-
-sub process_contents_file ( $ )
-{ my ($file) = check_args(1, @_);
-
- # could also use File::Basename
- my $info_file = $file;
- $info_file =~ s/(\/?index)?\.html$//;
- if ($info_file eq "")
- { chomp($info_file = `pwd`); }
- $info_file =~ s/^.*\///; # not the most efficient way to remove dirs
-
- $html_directory = $file;
- $html_directory =~ s/(\/|^)[^\/]+$/$1/;
-
- my $texi_file = "$info_file.texi";
- open(TEXI, ">$texi_file");
-
- print TEXI "\\input texinfo \@c -*-texinfo-*-\n";
- print TEXI "\@c %**start of header\n";
- print TEXI "\@setfilename $info_file\n";
-
- # 2. Summary Description and Copyright
- # The "Summary Description and Copyright" segment describes the
- # document and contains the copyright notice and copying permissions
- # for the Info file. The segment must be enclosed between `@ifinfo'
- # and `@end ifinfo' commands so that the formatters place it only in
- # the Info file.
- #
- # The summary description and copyright segment does not appear in the
- # printed document.
- #
- # @ifinfo
- # This is a short example of a complete Texinfo file.
- #
- # Copyright @copyright{} 1990 Free Software Foundation, Inc.
- # @end ifinfo
-
-
- # 3. Title and Copyright
- # The "Title and Copyright" segment contains the title and copyright
- # pages and copying permissions for the printed manual. The segment
- # must be enclosed between `@titlepage' and `@end titlepage'
- # commands. The title and copyright page appear only in the printed
- # manual.
- #
- # The titlepage segment does not appear in the Info file.
- #
- # @titlepage
- # @sp 10
- # @comment The title is printed in a large font.
- # @center @titlefont{Sample Title}
- #
- # @c The following two commands start the copyright page.
- # @page
- # @vskip 0pt plus 1filll
- # Copyright @copyright{} 1990 Free Software Foundation, Inc.
- # @end titlepage
-
-
- # 4. `Top' Node and Master Menu
- # The "Master Menu" contains a complete menu of all the nodes in the
- # whole Info file. It appears only in the Info file, in the `Top'
- # node.
- #
- # The `Top' node contains the master menu for the Info file. Since a
- # printed manual uses a table of contents rather than a menu, the master
- # menu appears only in the Info file.
- #
- # @node Top, First Chapter, , (dir)
- # @comment node-name, next, previous, up
- #
- # @menu
- # * First Chapter:: The first chapter is the
- # only chapter in this sample.
- # * Concept Index:: This index has two entries.
- # @end menu
-
-
-
- $current_ref_tdf = [ "Top", 0, $ARGV[0] ];
- process_section_file($file, 0, "Top");
- while (scalar(@contents_list))
- { $current_ref_tdf = shift @contents_list;
- process_section_file($ {$current_ref_tdf}[2], $ {$current_ref_tdf}[1], $ {$current_ref_tdf}[0]);
- }
-
- print TEXI "\n";
- for my $indextitle (@index_titles)
- { print TEXI "\@node $indextitle\n";
- print TEXI "\@unnumbered $indextitle\n";
- print TEXI "\@printindex $ {$index_info{$indextitle}}[1]\n";
- print TEXI "\n"; }
-
- print TEXI "\@contents\n";
- print TEXI "\@bye\n";
- close(TEXI);
-}
-
-# This needs to be last so global variable initializations are reached.
-
-if (scalar(@ARGV) == 0)
-{ die "No arguments supplied to html2texi.pl"; }
-
-if ($ARGV[0] eq "-test")
-{ my @test_args = @ARGV[1..$#ARGV];
- if (scalar(@test_args) == 0)
- { test("", "index.html"); }
- elsif (scalar(@test_args) == 1)
- { test("", $test_args[0]); }
- elsif (scalar(@test_args) == 2)
- { test($test_args[0], $test_args[1]); }
- else
- { die "Too many test arguments passed to html2texi: ", join(" ", @ARGV); }
- exit();
-}
-
-if (scalar(@ARGV) != 1)
-{ die "Pass one argument, the main/contents page"; }
-
-process_contents_file($ARGV[0]);
-
-# end of html2texi.pl
+++ /dev/null
-#! /usr/bin/env python
-
-"""Combine similar index entries into an entry and subentries.
-
-For example:
-
- \item {foobar} (in module flotz), 23
- \item {foobar} (in module whackit), 4323
-
-becomes
-
- \item {foobar}
- \subitem in module flotz, 23
- \subitem in module whackit, 4323
-
-Note that an item which matches the format of a collapsable item but which
-isn't part of a group of similar items is not modified.
-"""
-__version__ = '$Revision: 1.1.1.1 $'
-
-import re
-import string
-import StringIO
-import sys
-
-
-def cmp_entries(e1, e2, lower=string.lower):
- return cmp(lower(e1[1]), lower(e2[1])) or cmp(e1, e2)
-
-
-def dump_entries(write, entries):
- if len(entries) == 1:
- write(" \\item %s (%s)%s\n" % entries[0])
- return
- write(" \item %s\n" % entries[0][0])
- # now sort these in a case insensitive manner:
- if len(entries) > 0:
- entries.sort(cmp_entries)
- for xxx, subitem, pages in entries:
- write(" \subitem %s%s\n" % (subitem, pages))
-
-
-breakable_re = re.compile(
- r" \\item (.*) [(](.*)[)]((?:(?:, \d+)|(?:, \\[a-z]*\{\d+\}))+)")
-
-
-def process(ifn, ofn=None):
- if ifn == "-":
- ifp = sys.stdin
- else:
- ifp = open(ifn)
- if ofn is None:
- ofn = ifn
- ofp = StringIO.StringIO()
- entries = []
- match = breakable_re.match
- write = ofp.write
- while 1:
- line = ifp.readline()
- if not line:
- break
- m = match(line)
- if m:
- entry = m.group(1, 2, 3)
- if entries and entries[-1][0] != entry[0]:
- dump_entries(write, entries)
- entries = []
- entries.append(entry)
- elif entries:
- dump_entries(write, entries)
- entries = []
- write(line)
- else:
- write(line)
- del write
- del match
- ifp.close()
- data = ofp.getvalue()
- ofp.close()
- if ofn == "-":
- ofp = sys.stdout
- else:
- ofp = open(ofn, "w")
- ofp.write(data)
- ofp.close()
-
-
-def main():
- import getopt
- outfile = None
- opts, args = getopt.getopt(sys.argv[1:], "o:")
- for opt, val in opts:
- if opt in ("-o", "--output"):
- outfile = val
- filename = args[0]
- outfile = outfile or filename
- process(filename, outfile)
-
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-# Generate the Python "info" documentation.
-
-TOPDIR=..
-TOOLSDIR=$(TOPDIR)/tools
-HTMLDIR=$(TOPDIR)/html
-
-MKINFO=$(TOOLSDIR)/mkinfo
-SCRIPTS=$(TOOLSDIR)/html2texi.pl $(TOOLSDIR)/checkargs.pm $(TOOLSDIR)/mkinfo \
- $(TOOLSDIR)/fixinfo.el
-
-all: python-api.info python-ext.info python-lib.info \
- python-ref.info python-tut.info \
- python-dist.info python-inst.info
-
-
-python-api.info: $(HTMLDIR)/api/api.html $(SCRIPTS)
- $(MKINFO) $<
-
-python-ext.info: $(HTMLDIR)/ext/ext.html $(SCRIPTS)
- $(MKINFO) $<
-
-python-lib.info: $(HTMLDIR)/lib/lib.html $(SCRIPTS)
- $(MKINFO) $<
-
-# Not built by default; the conversion doesn't really handle it well.
-python-mac.info: $(HTMLDIR)/mac/mac.html $(SCRIPTS)
- $(MKINFO) $<
-
-python-ref.info: $(HTMLDIR)/ref/ref.html $(SCRIPTS)
- $(MKINFO) $<
-
-python-tut.info: $(HTMLDIR)/tut/tut.html $(SCRIPTS)
- $(MKINFO) $<
-
-python-dist.info: $(HTMLDIR)/dist/dist.html $(SCRIPTS)
- $(MKINFO) $<
-
-python-inst.info: $(HTMLDIR)/inst/inst.html $(SCRIPTS)
- $(MKINFO) $<
-
-clean:
- rm -f *.texi~ *.texi
-
-clobber: clean
- rm -f *.texi python-*.info python-*.info-[0-9]*
-
-
-# This makes sure we can build info files from a "clean" tree,
-# in case we haven't already built the HTML:
-
-$(HTMLDIR)/api/api.html:
- (cd $(HTMLDIR); $(MAKE) api)
-
-$(HTMLDIR)/ext/ext.html:
- (cd $(HTMLDIR); $(MAKE) ext)
-
-$(HTMLDIR)/lib/lib.html:
- (cd $(HTMLDIR); $(MAKE) lib)
-
-$(HTMLDIR)/mac/mac.html:
- (cd $(HTMLDIR); $(MAKE) mac)
-
-$(HTMLDIR)/ref/ref.html:
- (cd $(HTMLDIR); $(MAKE) ref)
-
-$(HTMLDIR)/tut/tut.html:
- (cd $(HTMLDIR); $(MAKE) tut)
-
-$(HTMLDIR)/dist/dist.html:
- (cd $(HTMLDIR); $(MAKE) dist)
-
-$(HTMLDIR)/inst/inst.html:
- (cd $(HTMLDIR); $(MAKE) inst)
+++ /dev/null
-This archive contains the standard Python documentation in GNU info
-format. Five manuals are included:
-
- python-ref.info* Python Reference Manual
- python-mac.info* Python Macintosh Modules
- python-lib.info* Python Library Reference
- python-ext.info* Extending and Embedding the Python Interpreter
- python-api.info* Python/C API Reference
- python-tut.info* Python Tutorial
-
-The file python.dir is a fragment of a "dir" file that can be used to
-incorporate these documents into an existing GNU info installation:
-insert the contents of this file into the "dir" or "localdir" file at
-an appropriate point and copy the python-*.info* files to the same
-directory.
-
-Thanks go to Milan Zamazal <pdm@freesoft.cz> for providing this
-conversion to the info format.
-
-Questions and comments on these documents should be directed to
-python-docs@python.org.
+++ /dev/null
-
-Python Standard Documentation
-
-* Python Library: (python-lib). Python Library Reference
-* Python Mac Modules: (python-mac). Python Macintosh Modules
-* Python Reference: (python-ref). Python Reference Manual
-* Python API: (python-api). Python/C API Reference Manual
-* Python Extending: (python-ext). Extending & Embedding Python
-* Python Tutorial: (python-tut). Python Tutorial
+++ /dev/null
-#! /usr/bin/env python
-
-# This Python program sorts and reformats the table of keywords in ref2.tex
-
-import string
-l = []
-try:
- while 1:
- l = l + string.split(raw_input())
-except EOFError:
- pass
-l.sort()
-for x in l[:]:
- while l.count(x) > 1: l.remove(x)
-ncols = 5
-nrows = (len(l)+ncols-1)/ncols
-for i in range(nrows):
- for j in range(i, len(l), nrows):
- print string.ljust(l[j], 10),
- print
+++ /dev/null
-#! /usr/bin/env python
-# -*- Python -*-
-#
-# This script can be used to identify undocumented modules in the Python
-# standard library. Use it like this:
-#
-# .../Doc/tools/listmodules --ignore-from .../Doc/paper-<paper>/modlib.idx
-
-"""%(program)s - list modules in the Python standard library
-
--a, --annotate Annotate the module names with the subdirectory they
- live in
--c, --categorize Group the modules by subdirectory
--i <file>,
-
---ignore-from <file> Ignore the modules listed in <file>. <file> may
- contain a list of module names or a module index file
- as produced when formatting the Python documentation
- (.idx or .html flavor).
-
-If neither -a nor -c are given, the modules are listed in alphabetical
-order.
-
-Note that -a and -c are mutually exclusive.
-
-Limitation: Modules loadable as shared objects may not be listed,
-though this script attempts to locate such modules.
-
-"""
-
-__version__ = '$Revision: 1.1.1.1 $'
-
-import getopt
-import glob
-import os
-import re
-import string
-import sys
-
-
-REMOVE_DIRS = ["dos-8x3", "encodings", "distutils",
- "lib-old", "lib-stdwin", "test"]
-
-
-def main():
- args = sys.argv[1:]
- annotate = 0
- builtin = 0
- categorize = 0
- ignore_dict = {}
- ignore = ignore_dict.has_key
- try:
- opts, args = getopt.getopt(
- args, "abchi:",
- ["annotate", "built-in", "categorize", "help", "ignore-from="])
- except getopt.error, msg:
- sys.stdout = sys.stderr
- print msg
- print
- usage()
- sys.exit(2)
- for opt, arg in opts:
- if opt in ("-a", "--annotate"):
- annotate = 1
- elif opt in ("-b", "--built-in"):
- builtin = 1
- elif opt in ("-c", "--categorize"):
- categorize = 1
- elif opt in ("-h", "--help"):
- usage()
- sys.exit()
- elif opt in ("-i", "--ignore-from"):
- data = open(arg).read()
- if data[:1] == "\\":
- ignore_from_idx(data, ignore_dict)
- else:
- ignore_from_modulelist(data, ignore_dict)
- if args or (annotate and categorize):
- usage()
- sys.exit(2)
- #
- # Populate the database:
- #
- srcdir = os.path.normpath(os.path.join(
- os.path.dirname(sys.argv[0]), os.pardir, os.pardir))
- os.chdir(srcdir)
- modules_by_name = {}
- modules_by_dir = {}
- if builtin:
- l = []
- modules_by_dir["<builtin>"] = l
- for name in sys.builtin_module_names:
- if not ignore(name):
- modules_by_name[name] = "<built-in>"
- l.append(name)
- rx = re.compile("Lib/plat-[a-zA-Z0-9]*/")
- fp = os.popen("find Lib -name \*.py -print", "r")
- while 1:
- line = fp.readline()
- if not line:
- break
- m = rx.match(line)
- if m:
- line = "Lib/plat-*/" + line[m.end():]
- line = line[4:-4] # strip off 'Lib/' and '.py\n'
- dir, name = os.path.split(line)
- dir = dir or "<standard>"
- if ignore(name):
- continue
- if dir not in REMOVE_DIRS:
- modules_by_name[name] = dir
- l = modules_by_dir.get(dir, [])
- modules_by_dir[dir] = l
- if name not in l:
- l.append(name)
- # load up extension modules:
- pwd = os.getcwd()
- try:
- os.chdir("Modules")
- dir = "<extension>"
- for line in glob.glob("*module.c"):
- name = line[:-8]
- if ignore(name) or modules_by_name.has_key(name) or name == "xx":
- continue
- modules_by_name[name] = dir
- l = modules_by_dir.get(dir, [])
- modules_by_dir[dir] = l
- if name not in l:
- l.append(name)
- finally:
- os.chdir(pwd)
- #
- # Dump the results:
- #
- if annotate:
- modules = modules_by_name.items()
- modules.sort()
- width = max(map(len, modules_by_name.keys()))
- format = "%%-%ds %%s" % width
- for name, dir in modules:
- if dir and dir[0] != "<":
- print format % (name, dir)
- else:
- print name
- elif categorize:
- modules = modules_by_dir.items()
- modules.sort()
- width = max(map(len, modules_by_dir.keys()))
- format = "%%-%ds %%s" % width
- for dir, names in modules:
- names.sort()
- print format % (dir, names[0])
- for name in names[1:]:
- print format % ('', name)
- print
- else:
- modules = modules_by_name.keys()
- modules.sort()
- print string.join(modules, "\n")
-
-
-def ignore_from_modulelist(data, ignore_dict):
- for name in string.split(data):
- ignore_dict[name] = name
-
-def ignore_from_idx(data, ignore_dict):
- data = string.replace(data, r"\hackscore {}", "_")
- rx = re.compile(r"\\indexentry\s*{([^@]*)@")
- for line in string.split(data, "\n"):
- m = rx.match(line)
- if m:
- name = m.group(1)
- ignore_dict[name] = name
-
-
-def usage():
- vars = {}
- vars["program"] = os.path.basename(sys.argv[0])
- print __doc__ % vars
-
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-#! /usr/bin/env python
-# -*- Python -*-
-
-import string
-import support
-import sys
-
-
-def collect(fp):
- names = []
- while 1:
- line = fp.readline()
- if not line:
- break
- line = string.strip(line)
- if line:
- names.append(line)
- else:
- names = []
- return names
-
-
-def main():
- options = support.Options()
- options.columns = 4
- options.variables["title"] = "Acknowledgements"
- options.parse(sys.argv[1:])
- names = collect(sys.stdin)
- percol = (len(names) + options.columns - 1) / options.columns
- colnums = []
- for i in range(options.columns):
- colnums.append(percol*i)
- fp = options.get_output_file()
- fp.write(string.rstrip(options.get_header()) + "\n")
- fp.write(THANKS + "\n")
- fp.write('<table width="100%" align="center">\n')
- for i in range(percol):
- fp.write(" <tr>\n")
- for j in colnums:
- try:
- fp.write(" <td>%s</td>\n" % names[i + j])
- except IndexError:
- pass
- fp.write(" </tr>\n")
- fp.write("</table>\n")
- fp.write(string.rstrip(options.get_footer()) + "\n")
- fp.close()
-
-THANKS = '''\
-
-<p>These people have contributed in some way to the Python
-documentation. This list is probably not complete -- if you feel that
-you or anyone else should be on this list, please let us know (send
-email to <a
-href="mailto:python-docs@python.org">python-docs@python.org</a>), and
-we will be glad to correct the problem.</p>
-
-<p>It is only with the input and contributions of the Python community
-that Python has such wonderful documentation -- <b>Thank You!</b></p>
-
-'''
-
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-#! /usr/bin/env python
-# -*- Python -*-
-"""usage: %(program)s [options...] file ...
-
-Options specifying formats to build:
- --html HyperText Markup Language (default)
- --pdf Portable Document Format
- --ps PostScript
- --dvi 'DeVice Indepentent' format from TeX
- --text ASCII text (requires lynx)
-
- More than one output format may be specified, or --all.
-
-HTML options:
- --address, -a Specify an address for page footers.
- --link Specify the number of levels to include on each page.
- --split, -s Specify a section level for page splitting, default: %(max_split_depth)s.
- --iconserver, -i Specify location of icons (default: ../).
- --image-type Specify the image type to use in HTML output;
- values: gif (default), png.
- --numeric Don't rename the HTML files; just keep node#.html for
- the filenames.
- --style Specify the CSS file to use for the output (filename,
- not a URL).
- --up-link URL to a parent document.
- --up-title Title of a parent document.
-
-Other options:
- --a4 Format for A4 paper.
- --letter Format for US letter paper (the default).
- --help, -H Show this text.
- --logging, -l Log stdout and stderr to a file (*.how).
- --debugging, -D Echo commands as they are executed.
- --keep, -k Keep temporary files around.
- --quiet, -q Do not print command output to stdout.
- (stderr is also lost, sorry; see *.how for errors)
-"""
-
-import getopt
-import glob
-import os
-import re
-import shutil
-import string
-import sys
-import tempfile
-
-
-MYDIR = os.path.abspath(sys.path[0])
-
-ISTFILE = os.path.join(MYDIR, "texinputs", "python.ist")
-NODE2LABEL_SCRIPT = os.path.join(MYDIR, "node2label.pl")
-L2H_INIT_FILE = os.path.join(MYDIR, "perl", "l2hinit.perl")
-
-BIBTEX_BINARY = "bibtex"
-DVIPS_BINARY = "dvips"
-LATEX_BINARY = "latex"
-LATEX2HTML_BINARY = "latex2html"
-LYNX_BINARY = "lynx"
-MAKEINDEX_BINARY = "makeindex"
-PDFLATEX_BINARY = "pdflatex"
-PERL_BINARY = "perl"
-PYTHON_BINARY = "python"
-
-
-def usage(options):
- print __doc__ % options
-
-def error(options, message, err=2):
- sys.stdout = sys.stderr
- print message
- print
- usage(options)
- sys.exit(2)
-
-
-class Options:
- program = os.path.basename(sys.argv[0])
- #
- address = ''
- builddir = None
- debugging = 0
- discard_temps = 1
- have_temps = 0
- icon_server = None
- image_type = "gif"
- logging = 0
- max_link_depth = 3
- max_split_depth = 6
- paper = "letter"
- quiet = 0
- runs = 0
- numeric = 0
- global_module_index = None
- style_file = os.path.join(MYDIR, "html", "style.css")
- about_file = os.path.join(MYDIR, "html", "about.dat")
- up_link = None
- up_title = None
- #
- DEFAULT_FORMATS = ("html",)
- ALL_FORMATS = ("dvi", "html", "pdf", "ps", "text")
-
- def __init__(self):
- self.formats = []
- self.l2h_init_files = []
-
- def __getitem__(self, key):
- # This is used when formatting the usage message.
- try:
- return getattr(self, key)
- except AttributeError:
- raise KeyError, key
-
- def parse(self, args):
- opts, args = getopt.getopt(args, "Hi:a:s:lDkqr:",
- ["all", "postscript", "help", "iconserver=",
- "address=", "a4", "letter", "l2h-init=",
- "link=", "split=", "logging", "debugging",
- "keep", "quiet", "runs=", "image-type=",
- "about=", "numeric", "style=", "paper=",
- "up-link=", "up-title=", "dir=",
- "global-module-index="]
- + list(self.ALL_FORMATS))
- for opt, arg in opts:
- if opt == "--all":
- self.formats = list(self.ALL_FORMATS)
- elif opt in ("-H", "--help"):
- usage(self)
- sys.exit()
- elif opt == "--iconserver":
- self.icon_server = arg
- elif opt in ("-a", "--address"):
- self.address = arg
- elif opt == "--a4":
- self.paper = "a4"
- elif opt == "--letter":
- self.paper = "letter"
- elif opt == "--link":
- self.max_link_depth = int(arg)
- elif opt in ("-s", "--split"):
- self.max_split_depth = int(arg)
- elif opt in ("-l", "--logging"):
- self.logging = self.logging + 1
- elif opt in ("-D", "--debugging"):
- self.debugging = self.debugging + 1
- elif opt in ("-k", "--keep"):
- self.discard_temps = 0
- elif opt in ("-q", "--quiet"):
- self.quiet = 1
- elif opt in ("-r", "--runs"):
- self.runs = int(arg)
- elif opt == "--image-type":
- self.image_type = arg
- elif opt == "--about":
- # always make this absolute:
- self.about_file = os.path.normpath(
- os.path.abspath(arg))
- elif opt == "--numeric":
- self.numeric = 1
- elif opt == "--style":
- self.style_file = os.path.abspath(arg)
- elif opt == "--l2h-init":
- self.l2h_init_files.append(os.path.abspath(arg))
- elif opt == "--up-link":
- self.up_link = arg
- elif opt == "--up-title":
- self.up_title = arg
- elif opt == "--global-module-index":
- self.global_module_index = arg
- elif opt == "--dir":
- self.builddir = arg
- elif opt == "--paper":
- self.paper = arg
- #
- # Format specifiers:
- #
- elif opt[2:] in self.ALL_FORMATS:
- self.add_format(opt[2:])
- elif opt == "--postscript":
- # synonym for --ps
- self.add_format("ps")
- self.initialize()
- #
- # return the args to allow the caller access:
- #
- return args
-
- def add_format(self, format):
- """Add a format to the formats list if not present."""
- if not format in self.formats:
- self.formats.append(format)
-
- def initialize(self):
- """Complete initialization. This is needed if parse() isn't used."""
- # add the default format if no formats were specified:
- if not self.formats:
- self.formats = self.DEFAULT_FORMATS
- # determine the base set of texinputs directories:
- texinputs = string.split(os.environ.get("TEXINPUTS", ""), os.pathsep)
- if not texinputs:
- texinputs = ['']
- self.base_texinputs = [
- os.path.join(MYDIR, "paper-" + self.paper),
- os.path.join(MYDIR, "texinputs"),
- ] + texinputs
-
-
-class Job:
- latex_runs = 0
-
- def __init__(self, options, path):
- self.options = options
- self.doctype = get_doctype(path)
- self.filedir, self.doc = split_pathname(path)
- self.log_filename = self.doc + ".how"
- if os.path.exists(self.log_filename):
- os.unlink(self.log_filename)
- if os.path.exists(self.doc + ".l2h"):
- self.l2h_aux_init_file = tempfile.mktemp()
- else:
- self.l2h_aux_init_file = self.doc + ".l2h"
- self.write_l2h_aux_init_file()
-
- def build(self):
- self.setup_texinputs()
- formats = self.options.formats
- if "dvi" in formats or "ps" in formats:
- self.build_dvi()
- if "pdf" in formats:
- self.build_pdf()
- if "ps" in formats:
- self.build_ps()
- if "html" in formats:
- self.require_temps()
- self.build_html(self.options.builddir or self.doc)
- if self.options.icon_server == ".":
- pattern = os.path.join(MYDIR, "html", "icons",
- "*." + self.options.image_type)
- imgs = glob.glob(pattern)
- if not imgs:
- self.warning(
- "Could not locate support images of type %s."
- % `self.options.image_type`)
- for fn in imgs:
- new_fn = os.path.join(self.doc, os.path.basename(fn))
- shutil.copyfile(fn, new_fn)
- if "text" in formats:
- self.require_temps()
- tempdir = self.doc
- need_html = "html" not in formats
- if self.options.max_split_depth != 1:
- fp = open(self.l2h_aux_init_file, "a")
- fp.write("# re-hack this file for --text:\n")
- l2hoption(fp, "MAX_SPLIT_DEPTH", "1")
- fp.write("1;\n")
- fp.close()
- tempdir = self.doc + "-temp-html"
- need_html = 1
- if need_html:
- self.build_html(tempdir, max_split_depth=1)
- self.build_text(tempdir)
- if self.options.discard_temps:
- self.cleanup()
-
- def setup_texinputs(self):
- texinputs = [self.filedir] + list(self.options.base_texinputs)
- os.environ["TEXINPUTS"] = string.join(['.']+texinputs, os.pathsep)
- self.message("TEXINPUTS=" + os.environ["TEXINPUTS"])
-
- def build_aux(self, binary=None):
- if binary is None:
- binary = LATEX_BINARY
- new_index( "%s.ind" % self.doc, "genindex")
- new_index("mod%s.ind" % self.doc, "modindex")
- self.run("%s %s" % (binary, self.doc))
- self.use_bibtex = check_for_bibtex(self.doc + ".aux")
- self.latex_runs = 1
-
- def build_dvi(self):
- self.use_latex(LATEX_BINARY)
-
- def build_pdf(self):
- self.use_latex(PDFLATEX_BINARY)
-
- def use_latex(self, binary):
- self.require_temps(binary=binary)
- if self.latex_runs < 2:
- if os.path.isfile("mod%s.idx" % self.doc):
- self.run("%s mod%s.idx" % (MAKEINDEX_BINARY, self.doc))
- use_indfix = 0
- if os.path.isfile(self.doc + ".idx"):
- use_indfix = 1
- # call to Doc/tools/fix_hack omitted; doesn't appear necessary
- self.run("%s %s.idx" % (MAKEINDEX_BINARY, self.doc))
- import indfix
- indfix.process(self.doc + ".ind")
- if self.use_bibtex:
- self.run("%s %s" % (BIBTEX_BINARY, self.doc))
- self.process_synopsis_files()
- #
- # let the doctype-specific handler do some intermediate work:
- #
- self.run("%s %s" % (binary, self.doc))
- self.latex_runs = self.latex_runs + 1
- if os.path.isfile("mod%s.idx" % self.doc):
- self.run("%s -s %s mod%s.idx"
- % (MAKEINDEX_BINARY, ISTFILE, self.doc))
- if use_indfix:
- self.run("%s -s %s %s.idx"
- % (MAKEINDEX_BINARY, ISTFILE, self.doc))
- indfix.process(self.doc + ".ind")
- self.process_synopsis_files()
- #
- # and now finish it off:
- #
- if os.path.isfile(self.doc + ".toc") and binary == PDFLATEX_BINARY:
- import toc2bkm
- if self.doctype == "manual":
- bigpart = "chapter"
- else:
- bigpart = "section"
- toc2bkm.process(self.doc + ".toc", self.doc + ".bkm", bigpart)
- if self.use_bibtex:
- self.run("%s %s" % (BIBTEX_BINARY, self.doc))
- self.run("%s %s" % (binary, self.doc))
- self.latex_runs = self.latex_runs + 1
-
- def process_synopsis_files(self):
- synopsis_files = glob.glob(self.doc + "*.syn")
- for path in synopsis_files:
- uniqify_module_table(path)
-
- def build_ps(self):
- self.run("%s -N0 -o %s.ps %s" % (DVIPS_BINARY, self.doc, self.doc))
-
- def build_html(self, builddir=None, max_split_depth=None):
- if builddir is None:
- builddir = self.doc
- if max_split_depth is None:
- max_split_depth = self.options.max_split_depth
- texfile = None
- for p in string.split(os.environ["TEXINPUTS"], os.pathsep):
- fn = os.path.join(p, self.doc + ".tex")
- if os.path.isfile(fn):
- texfile = fn
- break
- if not texfile:
- self.warning("Could not locate %s.tex; aborting." % self.doc)
- sys.exit(1)
- # remove leading ./ (or equiv.); might avoid problems w/ dvips
- if texfile[:2] == os.curdir + os.sep:
- texfile = texfile[2:]
- # build the command line and run LaTeX2HTML:
- if not os.path.isdir(builddir):
- os.mkdir(builddir)
- else:
- for fname in glob.glob(os.path.join(builddir, "*.html")):
- os.unlink(fname)
- args = [LATEX2HTML_BINARY,
- "-init_file", self.l2h_aux_init_file,
- "-dir", builddir,
- texfile
- ]
- self.run(string.join(args)) # XXX need quoting!
- # ... postprocess
- shutil.copyfile(self.options.style_file,
- os.path.join(builddir, self.doc + ".css"))
- shutil.copyfile(os.path.join(builddir, self.doc + ".html"),
- os.path.join(builddir, "index.html"))
- if max_split_depth != 1:
- if self.options.numeric:
- label_file = os.path.join(builddir, "labels.pl")
- fp = open(label_file)
- about_node = None
- target = " = q/about/;\n"
- x = len(target)
- while 1:
- line = fp.readline()
- if not line:
- break
- if line[-x:] == target:
- line = fp.readline()
- m = re.search(r"\|(node\d+\.[a-z]+)\|", line)
- about_node = m.group(1)
- shutil.copyfile(os.path.join(builddir, about_node),
- os.path.join(builddir, "about.html"))
- break
- else:
- pwd = os.getcwd()
- try:
- os.chdir(builddir)
- self.run("%s %s *.html" % (PERL_BINARY, NODE2LABEL_SCRIPT))
- finally:
- os.chdir(pwd)
-
- def build_text(self, tempdir=None):
- if tempdir is None:
- tempdir = self.doc
- indexfile = os.path.join(tempdir, "index.html")
- self.run("%s -nolist -dump %s >%s.txt"
- % (LYNX_BINARY, indexfile, self.doc))
-
- def require_temps(self, binary=None):
- if not self.latex_runs:
- self.build_aux(binary=binary)
-
- def write_l2h_aux_init_file(self):
- options = self.options
- fp = open(self.l2h_aux_init_file, "w")
- d = string_to_perl(os.path.dirname(L2H_INIT_FILE))
- fp.write("package main;\n"
- "push (@INC, '%s');\n"
- "$mydir = '%s';\n"
- % (d, d))
- fp.write(open(L2H_INIT_FILE).read())
- for filename in options.l2h_init_files:
- fp.write("\n# initialization code incorporated from:\n# ")
- fp.write(filename)
- fp.write("\n")
- fp.write(open(filename).read())
- fp.write("\n"
- "# auxillary init file for latex2html\n"
- "# generated by mkhowto\n"
- "$NO_AUTO_LINK = 1;\n"
- )
- l2hoption(fp, "ABOUT_FILE", options.about_file)
- l2hoption(fp, "ICONSERVER", options.icon_server)
- l2hoption(fp, "IMAGE_TYPE", options.image_type)
- l2hoption(fp, "ADDRESS", options.address)
- l2hoption(fp, "MAX_LINK_DEPTH", options.max_link_depth)
- l2hoption(fp, "MAX_SPLIT_DEPTH", options.max_split_depth)
- l2hoption(fp, "EXTERNAL_UP_LINK", options.up_link)
- l2hoption(fp, "EXTERNAL_UP_TITLE", options.up_title)
- l2hoption(fp, "GLOBAL_MODULE_INDEX", options.global_module_index)
- fp.write("1;\n")
- fp.close()
-
- def cleanup(self):
- self.__have_temps = 0
- for pattern in ("%s.aux", "%s.log", "%s.out", "%s.toc", "%s.bkm",
- "%s.idx", "%s.ilg", "%s.ind", "%s.pla",
- "%s.bbl", "%s.blg",
- "mod%s.idx", "mod%s.ind", "mod%s.ilg",
- ):
- safe_unlink(pattern % self.doc)
- map(safe_unlink, glob.glob(self.doc + "*.syn"))
- for spec in ("IMG*", "*.pl", "WARNINGS", "index.dat", "modindex.dat"):
- pattern = os.path.join(self.doc, spec)
- map(safe_unlink, glob.glob(pattern))
- if "dvi" not in self.options.formats:
- safe_unlink(self.doc + ".dvi")
- if os.path.isdir(self.doc + "-temp-html"):
- shutil.rmtree(self.doc + "-temp-html", ignore_errors=1)
- if not self.options.logging:
- os.unlink(self.log_filename)
- if not self.options.debugging:
- os.unlink(self.l2h_aux_init_file)
-
- def run(self, command):
- self.message(command)
- rc = os.system("(%s) </dev/null >>%s 2>&1"
- % (command, self.log_filename))
- if rc:
- self.warning(
- "Session transcript and error messages are in %s."
- % self.log_filename)
- sys.stderr.write("The relevant lines from the transcript are:\n")
- sys.stderr.write("-" * 72 + "\n")
- sys.stderr.writelines(get_run_transcript(self.log_filename))
- sys.exit(rc)
-
- def message(self, msg):
- msg = "+++ " + msg
- if not self.options.quiet:
- print msg
- self.log(msg + "\n")
-
- def warning(self, msg):
- msg = "*** %s\n" % msg
- sys.stderr.write(msg)
- self.log(msg)
-
- def log(self, msg):
- fp = open(self.log_filename, "a")
- fp.write(msg)
- fp.close()
-
-
-def get_run_transcript(filename):
- """Return lines from the transcript file for the most recent run() call."""
- fp = open(filename)
- lines = fp.readlines()
- fp.close()
- lines.reverse()
- L = []
- for line in lines:
- L.append(line)
- if line[:4] == "+++ ":
- break
- L.reverse()
- return L
-
-
-def safe_unlink(path):
- """Unlink a file without raising an error if it doesn't exist."""
- try:
- os.unlink(path)
- except os.error:
- pass
-
-
-def split_pathname(path):
- path = os.path.normpath(os.path.join(os.getcwd(), path))
- dirname, basename = os.path.split(path)
- if basename[-4:] == ".tex":
- basename = basename[:-4]
- return dirname, basename
-
-
-_doctype_rx = re.compile(r"\\documentclass(?:\[[^]]*\])?{([a-zA-Z]*)}")
-def get_doctype(path):
- fp = open(path)
- doctype = None
- while 1:
- line = fp.readline()
- if not line:
- break
- m = _doctype_rx.match(line)
- if m:
- doctype = m.group(1)
- break
- fp.close()
- return doctype
-
-
-def main():
- options = Options()
- try:
- args = options.parse(sys.argv[1:])
- except getopt.error, msg:
- error(options, msg)
- if not args:
- # attempt to locate single .tex file in current directory:
- args = glob.glob("*.tex")
- if not args:
- error(options, "No file to process.")
- if len(args) > 1:
- error(options, "Could not deduce which files should be processed.")
- #
- # parameters are processed, let's go!
- #
- for path in args:
- Job(options, path).build()
-
-
-def l2hoption(fp, option, value):
- if value:
- fp.write('$%s = "%s";\n' % (option, string_to_perl(str(value))))
-
-
-_to_perl = {}
-for c in map(chr, range(1, 256)):
- _to_perl[c] = c
-_to_perl["@"] = "\\@"
-_to_perl["$"] = "\\$"
-_to_perl['"'] = '\\"'
-
-def string_to_perl(s):
- return string.join(map(_to_perl.get, s), '')
-
-
-def check_for_bibtex(filename):
- fp = open(filename)
- pos = string.find(fp.read(), r"\bibdata{")
- fp.close()
- return pos >= 0
-
-def uniqify_module_table(filename):
- lines = open(filename).readlines()
- if len(lines) > 1:
- if lines[-1] == lines[-2]:
- del lines[-1]
- open(filename, "w").writelines(lines)
-
-
-def new_index(filename, label="genindex"):
- fp = open(filename, "w")
- fp.write(r"""\
-\begin{theindex}
-\label{%s}
-\end{theindex}
-""" % label)
- fp.close()
-
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-#! /bin/sh
-# -*- Ksh -*-
-
-# Script to drive the HTML-info conversion process.
-# Pass in a single parameter: the name of the top-level HTML file
-# generated by LaTeX2HTML.
-#
-# Written by Fred L. Drake, Jr. <fdrake@acm.org>
-
-PERL=${PERL:-perl}
-EMACS=${EMACS:-emacs}
-MAKEINFO=${MAKEINFO:-makeinfo}
-
-
-# Normalize file name since something called by html2texi.pl seems to
-# screw up with relative path names.
-FILENAME="$1"
-DOCDIR=`dirname "$FILENAME"`
-DOCFILE=`basename "$FILENAME"`
-DOCNAME=`basename "$FILENAME" .html`
-
-# Now build the real directory names, and locate our support stuff:
-WORKDIR=`pwd`
-cd `dirname $0`
-TOOLSDIR=`pwd`
-cd $DOCDIR
-DOCDIR=`pwd`
-cd $WORKDIR
-
-
-run() {
- # show what we're doing, like make does:
- echo "$*"
- $* || exit $?
-}
-
-
-# generate the Texinfo file:
-
-run $PERL -I$TOOLSDIR $TOOLSDIR/html2texi.pl $DOCDIR/$DOCFILE
-run $EMACS -batch -l $TOOLSDIR/fixinfo.el $DOCNAME.texi
-rm -f $DOCNAME.texi~
-
-
-# generate the .info files:
-
-run $MAKEINFO --footnote-style end --fill-column 72 \
- --paragraph-indent 0 $DOCNAME.texi
+++ /dev/null
-#! /usr/bin/env python
-# -*- Python -*-
-
-"""usage: %(program)s [options] file...
-
-Supported options:
-
- --address addr
- -a addr Set the address text to include at the end of the generated
- HTML; this should be used for contact information.
- --columns cols
- -c cols Set the number of columns each index section should be
- displayed in. The default is 1.
- --help
- -h Display this help message.
- --letters
- -l Split the output into sections by letter.
- --output file
- -o file Write output to 'file' instead of standard out.
- --iconserver is Use 'is' as the directory containing icons for the
- navigation bar. The default is 'icons'.
- --title str Set the page title to 'str'. The default is 'Global
- Module Index'.
- --uplink url Set the upward link URL. The default is './'.
- --uptitle str Set the upward link title. The default is 'Python
- Documentation Index'.
-"""
-import buildindex
-import os
-import re
-import string
-import support
-import sys
-
-
-class IndexOptions(support.Options):
- def __init__(self):
- support.Options.__init__(self)
- self.add_args("l", ["letters"])
- self.letters = 0
-
- def handle_option(self, opt, val):
- if opt in ("-l", "--letters"):
- self.letters = 1
-
- def usage(self):
- program = os.path.basename(sys.argv[0])
- print __doc__ % {"program": program}
-
-
-class Node(buildindex.Node):
- annotation = ""
-
- def __init__(self, link, str, seqno):
- parts = string.split(str, None, 1)
- if parts[0][-5:] == "</tt>":
- self.modname = parts[0][:-5]
- else:
- self.modname = parts[0]
- if len(parts) == 2:
- self.annotation = parts[1]
- buildindex.Node.__init__(self, link, self.modname, seqno)
-
- def __str__(self):
- return '<tt class="module">%s</tt> %s' \
- % (self.modname, self.annotation)
-
-_rx = re.compile(
- "<dt><a href='(module-.*\.html)#l2h-\d+'><tt class='module'>"
- "([a-zA-Z_][a-zA-Z0-9_.]*</tt>(\s*<em>"
- "\(<span class='platform'>.*</span>\)</em>)?)</a>")
-
-def main():
- options = IndexOptions()
- options.variables["title"] = "Global Module Index"
- options.parse(sys.argv[1:])
- args = options.args
- if not args:
- args = ["-"]
- #
- # Collect the input data:
- #
- nodes = []
- seqno = 0
- has_plat_flag = 0
- for ifn in args:
- if ifn == "-":
- ifp = sys.stdin
- dirname = ''
- else:
- ifp = open(ifn)
- dirname = os.path.dirname(ifn)
- while 1:
- line = ifp.readline()
- if not line:
- break
- m = _rx.match(line)
- if m:
- # This line specifies a module!
- basename, modname = m.group(1, 2)
- has_plat_flag = has_plat_flag or m.group(3)
- linkfile = os.path.join(dirname, basename)
- nodes.append(Node('<a href="%s">' % linkfile, modname, seqno))
- seqno = seqno + 1
- ifp.close()
- #
- # Generate all output:
- #
- num_nodes = len(nodes)
- # Here's the HTML generation:
- parts = [options.get_header(),
- buildindex.process_nodes(nodes, options.columns, options.letters),
- options.get_footer(),
- ]
- if has_plat_flag:
- parts.insert(1, PLAT_DISCUSS)
- html = string.join(parts, '')
- program = os.path.basename(sys.argv[0])
- fp = options.get_output_file()
- fp.write(string.rstrip(html) + "\n")
- if options.outputfile == "-":
- sys.stderr.write("%s: %d index nodes\n" % (program, num_nodes))
- else:
- print
- print "%s: %d index nodes" % (program, num_nodes)
-
-
-PLAT_DISCUSS = """
-<p> Some module names are followed by an annotation indicating what
-platform they are available on.</p>
-
-"""
-
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-#! /usr/bin/env python
-# -*- Python -*-
-
-"""%(program)s - script to create the latex source distribution
-
-usage:
- %(program)s [-t|--tools] release [tag]
-
-with -t|--tools: doesn't include the documents, only the framework
-
-without [tag]: generate from the current version that's checked in
- (*NOT* what's in the current directory!)
-
-with [tag]: generate from the named tag
-"""
-#* should be modified to get the Python version number automatically
-# from the Makefile or someplace.
-
-import getopt
-import glob
-import os
-import re
-import shutil
-import sys
-import tempfile
-
-import cvsinfo
-
-
-quiet = 0
-rx = re.compile(r":ext:(?:[a-zA-Z0-9]+)@cvs\.([a-zA-Z0-9]+).sourceforge.net:"
- r"/cvsroot/\1")
-
-
-def main():
- global quiet
- try:
- opts, args = getopt.getopt(sys.argv[1:], "abgtzq",
- ["all", "bzip2", "gzip", "tools", "zip",
- "quiet"])
- except getopt.error, e:
- usage(warning=str(e))
- sys.exit(2)
- if len(args) not in (1, 2):
- usage(warning="wrong number of parameters")
- sys.exit(2)
- tools = 0
- formats = {}
- for opt, arg in opts:
- if opt in ("-t", "--tools"):
- tools = 1
- elif opt in ("-q", "--quiet"):
- quiet = quiet + 1
- elif opt in ("-b", "--bzip2"):
- formats["bzip2"] = 1
- elif opt in ("-g", "--gzip"):
- formats["gzip"] = 1
- elif opt in ("-z", "--zip"):
- formats["zip"] = 1
- elif opt in ("-a", "--all"):
- formats["bzip2"] = 1
- formats["gzip"] = 1
- formats["zip"] = 1
- if formats:
- # make order human-predictable
- formats = formats.keys()
- formats.sort()
- else:
- formats = ["gzip"]
- release = args[0]
- cvstag = None
- if len(args) > 1:
- cvstag = args[1]
- tempdir = tempfile.mktemp()
- os.mkdir(tempdir)
- pkgdir = os.path.join(tempdir, "Python-" + release)
- os.mkdir(pkgdir)
- pwd = os.getcwd()
- mydir = os.path.abspath(os.path.dirname(sys.argv[0]))
- info = cvsinfo.RepositoryInfo(mydir)
- cvsroot = info.get_cvsroot()
- m = rx.match(cvsroot)
- if m:
- # If this is an authenticated SourceForge repository, convert to
- # anonymous usage for the export/checkout, since that avoids the
- # SSH overhead.
- group = m.group(1)
- cvsroot = ":pserver:anonymous@cvs.%s.sourceforge.net:/cvsroot/%s" \
- % (group, group)
- # For some reason, SourceForge/CVS doesn't seem to care that we
- # might not have done a "cvs login" to the anonymous server.
- # That avoids a lot of painful gunk here.
- os.chdir(pkgdir)
- if not quiet:
- print "--- current directory is:", pkgdir
- if cvstag:
- run("cvs -d%s export -r %s -d Doc python/dist/src/Doc"
- % (cvsroot, cvstag))
- else:
- run("cvs -Q -d%s checkout -d Doc python/dist/src/Doc" % cvsroot)
- # remove CVS directories
- for p in ('*/CVS', '*/*/CVS', '*/*/*/CVS'):
- map(shutil.rmtree, glob.glob(p))
- for f in ('.cvsignore', '*/.cvsignore'):
- map(os.unlink, glob.glob(f))
- LICENSE = os.path.normpath(
- os.path.join(mydir, os.pardir, os.pardir, "LICENSE"))
- shutil.copyfile(LICENSE, "Doc/LICENSE")
- if tools:
- archive = "doctools-" + release
- # we don't want the actual documents in this case:
- for d in ("api", "dist", "doc", "ext", "inst",
- "lib", "mac", "ref", "tut"):
- shutil.rmtree(os.path.join(os.path.join(pkgdir, "Doc"), d))
- else:
- archive = "latex-" + release
-
- # XXX should also remove the .cvsignore files at this point
-
- os.chdir(tempdir)
- archive = os.path.join(pwd, archive)
- for format in formats:
- if format == "bzip2":
- run("tar cf - Python-%s | bzip2 -9 >%s.tar.bz2"
- % (release, archive))
- elif format == "gzip":
- run("tar cf - Python-%s | gzip -9 >%s.tgz"
- % (release, archive))
- elif format == "zip":
- if os.path.exists(archive + ".zip"):
- os.unlink(archive + ".zip")
- run("zip -q -r9 %s.zip Python-%s"
- % (archive, release))
-
- # clean up the work area:
- os.chdir(pwd)
- shutil.rmtree(tempdir)
-
-
-def run(cmd):
- if quiet < 2:
- print "+++", cmd
- if quiet:
- cmd = "%s >/dev/null" % cmd
- rc = os.system(cmd)
- if rc:
- sys.exit(rc)
-
-
-def usage(warning=None):
- stdout = sys.stdout
- sys.stdout = sys.stderr
- program = os.path.basename(sys.argv[0])
- try:
- if warning:
- print "%s: %s\n" % (program, warning)
- print __doc__ % {"program": program}
- finally:
- sys.stdout = stdout
-
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-#! /usr/bin/env perl
-
-use English;
-$INPLACE_EDIT = '';
-
-# read the labels, then reverse the mappings
-require "labels.pl";
-
-%nodes = ();
-my $key;
-# sort so that we get a consistent assignment for nodes with multiple labels
-foreach $label (sort keys %external_labels) {
- $key = $external_labels{$label};
- $key =~ s|^/||;
- $nodes{$key} = $label;
-}
-
-# This adds the "internal" labels added for indexing. These labels will not
-# be used for file names.
-require "internals.pl";
-foreach $label (keys %internal_labels) {
- $key = $internal_labels{$label};
- $key =~ s|^/||;
- if (defined($nodes{$key})) {
- $nodes{$label} = $nodes{$key};
- }
-}
-
-# collect labels that have been used
-%newnames = ();
-
-while (<>) {
- # don't want to do one s/// per line per node
- # so look for lines with hrefs, then do s/// on nodes present
- if (/(HREF|href)=[\"\']([^\#\"\']*)html[\#\"\']/) {
- @parts = split(/(HREF|href)\=[\"\']/);
- shift @parts;
- for $node (@parts) {
- $node =~ s/[\#\"\'].*$//g;
- chop($node);
- if (defined($nodes{$node})) {
- $label = $nodes{$node};
- if (s/(HREF|href)=([\"\'])$node([\#\"\'])/href=$2$label.html$3/g) {
- s/(HREF|href)=([\"\'])$label.html/href=$2$label.html/g;
- $newnames{$node} = "$label.html";
- }
- }
- }
- }
- print;
-}
-
-foreach $oldname (keys %newnames) {
- rename($oldname, $newnames{$oldname});
-}
+++ /dev/null
-%
-% Change this to say a4paper instead of letterpaper if you want A4.
-%
-\newcommand{\py@paper}{a4paper}
-\newcommand{\py@ptsize}{10pt}
+++ /dev/null
-package SynopsisTable;
-
-sub new{
- return bless {names=>'', info=>{}, file=>''};
-}
-
-sub declare{
- my($self,$name,$key,$type) = @_;
- if ($self->{names}) {
- $self->{names} .= ",$name";
- }
- else {
- $self->{names} .= "$name";
- }
- $self->{info}{$name} = "$key,$type,";
-}
-
-# The 'file' attribute is used to store the filename of the node in which
-# the table will be presented; this assumes that each table will be presented
-# only once, which works for the current use of this object.
-
-sub set_file{
- my($self, $filename) = @_;
- $self->{file} = "$filename";
-}
-
-sub get_file{
- my $self = shift;
- return $self->{file};
-}
-
-sub set_synopsis{
- my($self,$name,$synopsis) = @_;
- my($key,$type,$unused) = split ',', $self->{info}{$name}, 3;
- $self->{info}{$name} = "$key,$type,$synopsis";
-}
-
-sub get{
- my($self,$name) = @_;
- return split /,/, $self->{info}{$name}, 3;
-}
-
-sub show{
- my $self = shift;
- my $name;
- print "names: ", $self->{names}, "\n\n";
- foreach $name (split /,/, $self->{names}) {
- my($key,$type,$synopsis) = $self->get($name);
- print "$name($key) is $type: $synopsis\n";
- }
-}
-
-sub tohtml{
- my $self = shift;
- my $data = "<table class='synopsistable'>\n";
- my $name;
- foreach $name (split /,/, $self->{names}) {
- my($key,$type,$synopsis) = $self->get($name);
- my $link = "<a href='module-$key.html'>";
- $data .= (' <tr>'
- . "<td><b><tt class='module'>$link$name</a></tt></b></td>\n"
- . " <td class='synopsis'>$synopsis</td></tr>\n");
- }
- $data .= "</table>\n";
- $data;
-}
-
-
-package testSynopsisTable;
-
-sub test{
- # this little test is mostly to debug the stuff above, since this is
- # my first Perl "object".
- my $st = SynopsisTable->new();
- $st->declare("sample", "sample", "standard");
- $st->set_synopsis("sample", "This is a little synopsis....");
- $st->declare("copy_reg", "copyreg", "standard");
- $st->set_synopsis("copy_reg", "pickle support stuff");
- $st->show();
-
- print "\n\n";
-
- my $st2 = SynopsisTable->new();
- $st2->declare("st2module", "st2module", "built-in");
- $st2->set_synopsis("st2module", "silly little synopsis");
- $st2->show();
-}
-
-1; # This must be the last line -- Perl is bogus!
+++ /dev/null
-# LaTeX2HTML support for distutils.sty.
-
-package main;
-
-sub do_cmd_command {
- return use_wrappers(@_[0], '<code>', '</code>');
-}
-
-sub do_cmd_option {
- return use_wrappers(@_[0], '<font face="sans-serif">', '</font>');
-}
-
-sub do_cmd_filevar {
- return use_wrappers(@_[0], '<font face="sans-serif"></i>', '</i></font>');
-}
-
-sub do_cmd_XXX {
- return use_wrappers(@_[0], '<b>** ', ' **</b>');
-}
-
-1;
+++ /dev/null
-# -*- perl -*-
-#
-# This implements the Python howto class. All it really needs to do it
-# load the "python" style.
-
-package main;
-
-do_require_package("article");
-do_require_package("alltt");
-do_require_package("python");
-
-1; # sheesh....
+++ /dev/null
-# LaTeX2HTML support base for use with Python documentation.
-
-package main;
-
-use L2hos;
-
-$HTML_VERSION = 4.0;
-
-$MAX_LINK_DEPTH = 2;
-$ADDRESS = '';
-
-$NO_FOOTNODE = 1;
-$NUMBERED_FOOTNOTES = 1;
-
-# Python documentation uses section numbers to support references to match
-# in the printed and online versions.
-#
-$SHOW_SECTION_NUMBERS = 1;
-
-$ICONSERVER = '../icons';
-$IMAGE_TYPE = 'gif';
-
-# Control where the navigation bars should show up:
-$TOP_NAVIGATION = 1;
-$BOTTOM_NAVIGATION = 1;
-$AUTO_NAVIGATION = 0;
-
-$BODYTEXT = '';
-$CHILDLINE = "\n<p><hr>\n";
-$VERBOSITY = 0;
-
-# default # of columns for the indexes
-$INDEX_COLUMNS = 2;
-$MODULE_INDEX_COLUMNS = 4;
-
-
-# A little painful, but lets us clean up the top level directory a little,
-# and not be tied to the current directory (as far as I can tell). Testing
-# an existing definition of $mydir is needed since it cannot be computed when
-# run under mkhowto with recent versions of LaTeX2HTML, since this file is
-# not read directly by LaTeX2HTML any more. mkhowto is required to prepend
-# the required definition at the top of the actual input file.
-#
-if (!defined $mydir) {
- use Cwd;
- use File::Basename;
- ($myname, $mydir, $myext) = fileparse(__FILE__, '\..*');
- chop $mydir; # remove trailing '/'
- $mydir = getcwd() . "$dd$mydir"
- unless $mydir =~ s|^/|/|;
-}
-$LATEX2HTMLSTYLES = "$mydir$envkey$LATEX2HTMLSTYLES";
-push (@INC, $mydir);
-
-($myrootname, $myrootdir, $myext) = fileparse($mydir, '\..*');
-chop $myrootdir;
-
-
-# Hackish way to get the appropriate paper-*/ directory into $TEXINPUTS;
-# pass in the paper size (a4 or letter) as the environment variable PAPER
-# to add the right directory. If not given, the current directory is
-# added instead for use with HOWTO processing.
-#
-if (defined $ENV{'PAPER'}) {
- $mytexinputs = "$myrootdir${dd}paper-$ENV{'PAPER'}$envkey";
-}
-else {
- $mytexinputs = getcwd() . $envkey;
-}
-$mytexinputs .= "$myrootdir${dd}texinputs";
-
-
-# Change this variable to change the text added in "About this document...";
-# this should be an absolute pathname to get it right.
-#
-$ABOUT_FILE = "$myrootdir${dd}html${dd}stdabout.dat";
-
-
-sub custom_driver_hook {
- #
- # This adds the directory of the main input file to $TEXINPUTS; it
- # seems to be sufficiently general that it should be fine for HOWTO
- # processing.
- #
- my $file = @_[0];
- my($jobname, $dir, $ext) = fileparse($file, '\..*');
- $dir = L2hos->Make_directory_absolute($dir);
- $dir =~ s/$dd$//;
- $TEXINPUTS = "$dir$envkey$mytexinputs";
- print "\nAdding $dir to \$TEXINPUTS\n";
-}
-
-
-$CUSTOM_BUTTONS = '';
-
-sub make_nav_sectref {
- my($label,$title) = @_;
- if ($title) {
- if ($title =~ /\<[aA] /) {
- $title =~ s/\<[aA] /<a class="sectref" /;
- }
- else {
- $title = "<span class=\"sectref\">$title</span>";
- }
- return "<b class=\"navlabel\">$label:</b> $title\n";
- }
- return '';
-}
-
-@my_icon_tags = ();
-$my_icon_tags{'next'} = 'Next Page';
-$my_icon_tags{'next_page'} = 'Next Page';
-$my_icon_tags{'previous'} = 'Previous Page';
-$my_icon_tags{'previous_page'} = 'Previous Page';
-$my_icon_tags{'up'} = 'Up One Level';
-$my_icon_tags{'contents'} = 'Contents';
-$my_icon_tags{'index'} = 'Index';
-$my_icon_tags{'modules'} = 'Module Index';
-
-@my_icon_names = ();
-$my_icon_names{'previous_page'} = 'previous';
-$my_icon_names{'next_page'} = 'next';
-
-sub get_my_icon {
- my $name = @_[0];
- my $text = $my_icon_tags{$name};
- if ($my_icon_names{$name}) {
- $name = $my_icon_names{$name};
- }
- if ($text eq '') {
- $name = 'blank';
- }
- my $iconserver = ($ICONSERVER eq '.') ? '' : "$ICONSERVER/";
- return "<img src=\"$iconserver$name.$IMAGE_TYPE\"\n border=\"0\""
- . " height=\"32\"\n alt=\"$text\" width=\"32\">";
-}
-
-sub use_my_icon {
- my $s = @_[0];
- if ($s =~ /\<tex2html_([a-z_]+)_visible_mark\>/) {
- my $r = get_my_icon($1);
- $s =~ s/\<tex2html_[a-z_]+_visible_mark\>/$r/;
- }
- return $s;
-}
-
-sub make_nav_panel {
- my $s;
- my $BLANK_ICON = get_my_icon('blank');
- $NEXT = $NEXT_TITLE ? use_my_icon("$NEXT") : $BLANK_ICON;
- $UP = $UP_TITLE ? use_my_icon("$UP") : $BLANK_ICON;
- $PREVIOUS = $PREVIOUS_TITLE ? use_my_icon("$PREVIOUS") : $BLANK_ICON;
- $CONTENTS = use_my_icon("$CONTENTS");
- $INDEX = $INDEX ? use_my_icon("$INDEX") : $BLANK_ICON;
- if (!$CUSTOM_BUTTONS) {
- $CUSTOM_BUTTONS = $BLANK_ICON;
- }
- $s = ('<table align="center" width="100%" cellpadding="0" cellspacing="2">'
- . "\n<tr>"
- # left-hand side
- . "\n<td>$PREVIOUS</td>"
- . "\n<td>$UP</td>"
- . "\n<td>$NEXT</td>"
- # title box
- . "\n<td align=\"center\" width=\"100%\">$t_title</td>"
- # right-hand side
- . "\n<td>$CONTENTS</td>"
- . "\n<td>$CUSTOM_BUTTONS</td>" # module index
- . "\n<td>$INDEX</td>"
- . "\n</tr></table>\n"
- # textual navigation
- . make_nav_sectref("Previous", $PREVIOUS_TITLE)
- . make_nav_sectref("Up", $UP_TITLE)
- . make_nav_sectref("Next", $NEXT_TITLE)
- );
- # remove these; they are unnecessary and cause errors from validation
- $s =~ s/ NAME="tex2html\d+"\n */ /g;
- return $s;
-}
-
-sub get_version_text {
- if ($PACKAGE_VERSION ne '' && $t_date) {
- return ("<span class=\"release-info\">"
- . "Release $PACKAGE_VERSION,"
- . " documentation updated on $t_date.</span>");
- }
- if ($PACKAGE_VERSION ne '') {
- return ("<span class=\"release-info\">"
- . "Release $PACKAGE_VERSION.</span>");
- }
- if ($t_date) {
- return ("<span class=\"release-info\">Documentation released on "
- . "$t_date.</span>");
- }
- return '';
-}
-
-
-sub top_navigation_panel {
- return "\n"
- . make_nav_panel()
- . "<br><hr>\n";
-}
-
-sub bot_navigation_panel {
- return "\n<p><hr>\n"
- . make_nav_panel()
- . "<hr>\n"
- . get_version_text()
- . "\n";
-}
-
-sub add_link {
- # Returns a pair (iconic link, textual link)
- my($icon, $current_file, @link) = @_;
- my($dummy, $file, $title) = split($delim,
- $section_info{join(' ',@link)});
- if ($icon =~ /\<tex2html_([_a-z]+)_visible_mark\>/) {
- my $r = get_my_icon($1);
- $icon =~ s/\<tex2html_[_a-z]+_visible_mark\>/$r/;
- }
- if ($title && ($file ne $current_file)) {
- $title = purify($title);
- $title = get_first_words($title, $WORDS_IN_NAVIGATION_PANEL_TITLES);
- return (make_href($file, $icon), make_href($file, "$title"))
- }
- elsif ($icon eq get_my_icon('up') && $EXTERNAL_UP_LINK) {
- return (make_href($EXTERNAL_UP_LINK, $icon),
- make_href($EXTERNAL_UP_LINK, "$EXTERNAL_UP_TITLE"))
- }
- elsif ($icon eq get_my_icon('previous')
- && $EXTERNAL_PREV_LINK && $EXTERNAL_PREV_TITLE) {
- return (make_href($EXTERNAL_PREV_LINK, $icon),
- make_href($EXTERNAL_PREV_LINK, "$EXTERNAL_PREV_TITLE"))
- }
- elsif ($icon eq get_my_icon('next')
- && $EXTERNAL_DOWN_LINK && $EXTERNAL_DOWN_TITLE) {
- return (make_href($EXTERNAL_DOWN_LINK, $icon),
- make_href($EXTERNAL_DOWN_LINK, "$EXTERNAL_DOWN_TITLE"))
- }
- return (&inactive_img($icon), "");
-}
-
-sub add_special_link {
- my($icon, $file, $current_file) = @_;
- if ($icon =~ /\<tex2html_([_a-z]+)_visible_mark\>/) {
- my $r = get_my_icon($1);
- $icon =~ s/\<tex2html_[_a-z]+_visible_mark\>/$r/;
- }
- return (($file && ($file ne $current_file))
- ? make_href($file, $icon)
- : undef)
-}
-
-# The img_tag() function seems only to be called with the parameter
-# 'anchor_invisible_mark', which we want to turn into ''. Since
-# replace_icon_marks() is the only interesting caller, and all it really
-# does is call img_tag(), we can just define the hook alternative to be
-# a no-op instead.
-#
-sub replace_icons_hook {}
-
-sub do_cmd_arabic {
- # get rid of that nasty <SPAN CLASS="arabic">...</SPAN>
- my($ctr, $val, $id, $text) = &read_counter_value(@_[0]);
- return ($val ? farabic($val) : "0") . $text;
-}
-
-
-sub gen_index_id {
- # this is used to ensure common index key generation and a stable sort
- my($str,$extra) = @_;
- sprintf('%s###%s%010d', $str, $extra, ++$global{'max_id'});
-}
-
-sub insert_index {
- my($mark,$datafile,$columns,$letters,$prefix) = @_;
- my $prog = "$myrootdir/tools/buildindex.py";
- my $index;
- if ($letters) {
- $index = `$prog --columns $columns --letters $datafile`;
- }
- else {
- $index = `$prog --columns $columns $datafile`;
- }
- if (!s/$mark/$prefix$index/) {
- print "\nCould not locate index mark: $mark";
- }
-}
-
-sub add_idx {
- print "\nBuilding HTML for the index ...";
- close(IDXFILE);
- insert_index($idx_mark, 'index.dat', $INDEX_COLUMNS, 1, '');
-}
-
-
-$idx_module_mark = '<tex2html_idx_module_mark>';
-$idx_module_title = 'Module Index';
-
-sub add_module_idx {
- print "\nBuilding HTML for the module index ...";
- my $key;
- my $first = 1;
- my $prevplat = '';
- my $allthesame = 1;
- my $prefix = '';
- foreach $key (keys %Modules) {
- $key =~ s/<tt>([a-zA-Z0-9._]*)<\/tt>/\1/;
- my $plat = "$ModulePlatforms{$key}";
- $plat = ''
- if ($plat eq $IGNORE_PLATFORM_ANNOTATION);
- if (!$first) {
- $allthesame = 0
- if ($prevplat ne $plat);
- }
- else { $first = 0; }
- $prevplat = $plat;
- }
- open(MODIDXFILE, '>modindex.dat') || die "\n$!\n";
- foreach $key (keys %Modules) {
- # dump the line in the data file; just use a dummy seqno field
- my $nkey = $1;
- my $moditem = "$Modules{$key}";
- my $plat = '';
- $key =~ s/<tt>([a-zA-Z0-9._]*)<\/tt>/\1/;
- if ($ModulePlatforms{$key} && !$allthesame) {
- $plat = (" <em>(<span class='platform'>$ModulePlatforms{$key}"
- . '</span>)</em>');
- }
- print MODIDXFILE $moditem . $IDXFILE_FIELD_SEP
- . "<tt class='module'>$key</tt>$plat###\n";
- }
- close(MODIDXFILE);
-
- if ($GLOBAL_MODULE_INDEX) {
- $prefix = <<MODULE_INDEX_PREFIX;
-
-<p> This index only lists modules documented in this manual.
- The <em class="citetitle"><a href="$GLOBAL_MODULE_INDEX">Global Module
- Index</a></em> lists all modules that are documented in this set
- of manuals.</p>
-MODULE_INDEX_PREFIX
- }
- if (!$allthesame) {
- $prefix .= <<PLAT_DISCUSS;
-
-<p> Some module names are followed by an annotation indicating what
-platform they are available on.</p>
-
-PLAT_DISCUSS
- }
- insert_index($idx_module_mark, 'modindex.dat', $MODULE_INDEX_COLUMNS, 0,
- $prefix);
-}
-
-# replace both indexes as needed:
-sub add_idx_hook {
- add_idx() if (/$idx_mark/);
- process_python_state();
- if ($MODULE_INDEX_FILE) {
- local ($_);
- open(MYFILE, "<$MODULE_INDEX_FILE");
- sysread(MYFILE, $_, 1024*1024);
- close(MYFILE);
- add_module_idx();
- open(MYFILE,">$MODULE_INDEX_FILE");
- print MYFILE $_;
- close(MYFILE);
- }
-}
-
-
-# In addition to the standard stuff, add label to allow named node files and
-# support suppression of the page complete (for HTML Help use).
-sub do_cmd_tableofcontents {
- local($_) = @_;
- $TITLE = $toc_title;
- $tocfile = $CURRENT_FILE;
- my($closures,$reopens) = preserve_open_tags();
- anchor_label('contents', $CURRENT_FILE, $_); # this is added
- join('', "<BR>\n\\tableofchildlinks[off]", $closures
- , make_section_heading($toc_title, 'H2'), $toc_mark
- , $reopens, $_);
-}
-# In addition to the standard stuff, add label to allow named node files.
-sub do_cmd_listoffigures {
- local($_) = @_;
- $TITLE = $lof_title;
- $loffile = $CURRENT_FILE;
- my($closures,$reopens) = preserve_open_tags();
- anchor_label('lof', $CURRENT_FILE, $_); # this is added
- join('', "<BR>\n", $closures
- , make_section_heading($lof_title, 'H2'), $lof_mark
- , $reopens, $_);
-}
-# In addition to the standard stuff, add label to allow named node files.
-sub do_cmd_listoftables {
- local($_) = @_;
- $TITLE = $lot_title;
- $lotfile = $CURRENT_FILE;
- my($closures,$reopens) = preserve_open_tags();
- anchor_label('lot', $CURRENT_FILE, $_); # this is added
- join('', "<BR>\n", $closures
- , make_section_heading($lot_title, 'H2'), $lot_mark
- , $reopens, $_);
-}
-# In addition to the standard stuff, add label to allow named node files.
-sub do_cmd_textohtmlinfopage {
- local($_) = @_;
- if ($INFO) { #
- anchor_label("about",$CURRENT_FILE,$_); # this is added
- } #
- my $the_version = ''; # and the rest is
- if ($t_date) { # mostly ours
- $the_version = ",\n$t_date";
- if ($PACKAGE_VERSION) {
- $the_version .= ", Release $PACKAGE_VERSION";
- }
- }
- $_ = (($INFO == 1)
- ? join('',
- $close_all,
- "<strong>$t_title</strong>$the_version\n",
- `cat $ABOUT_FILE`,
- $open_all, $_)
- : join('', $close_all, $INFO,"\n", $open_all, $_));
- $_;
-}
-
-# $idx_mark will be replaced with the real index at the end
-sub do_cmd_textohtmlindex {
- local($_) = @_;
- $TITLE = $idx_title;
- $idxfile = $CURRENT_FILE;
- if (%index_labels) { make_index_labels(); }
- if (($SHORT_INDEX) && (%index_segment)) { make_preindex(); }
- else { $preindex = ''; }
- my $heading = make_section_heading($idx_title, 'h2') . $idx_mark;
- my($pre,$post) = minimize_open_tags($heading);
- anchor_label('genindex',$CURRENT_FILE,$_); # this is added
- return "<br>\n" . $pre . $_;
-}
-
-$MODULE_INDEX_FILE = '';
-
-# $idx_module_mark will be replaced with the real index at the end
-sub do_cmd_textohtmlmoduleindex {
- local($_) = @_;
- $TITLE = $idx_module_title;
- anchor_label('modindex', $CURRENT_FILE, $_);
- $MODULE_INDEX_FILE = "$CURRENT_FILE";
- $_ = ('<p>' . make_section_heading($idx_module_title, 'h2')
- . $idx_module_mark . $_);
- return $_;
-}
-
-# The bibliography and the index should be treated as separate
-# sections in their own HTML files. The \bibliography{} command acts
-# as a sectioning command that has the desired effect. But when the
-# bibliography is constructed manually using the thebibliography
-# environment, or when using the theindex environment it is not
-# possible to use the normal sectioning mechanism. This subroutine
-# inserts a \bibliography{} or a dummy \textohtmlindex command just
-# before the appropriate environments to force sectioning.
-
-# XXX This *assumes* that if there are two {theindex} environments,
-# the first is the module index and the second is the standard
-# index. This is sufficient for the current Python documentation,
-# but that's about it.
-
-sub add_bbl_and_idx_dummy_commands {
- my $id = $global{'max_id'};
-
- s/([\\]begin\s*$O\d+$C\s*thebibliography)/$bbl_cnt++; $1/eg;
- s/([\\]begin\s*$O\d+$C\s*thebibliography)/$id++; "\\bibliography$O$id$C$O$id$C $1"/geo;
- my(@parts) = split(/\\begin\s*$O\d+$C\s*theindex/);
- if (scalar(@parts) == 3) {
- # Be careful to re-write the string in place, since $_ is *not*
- # returned explicity; *** nasty side-effect dependency! ***
- print "\nadd_bbl_and_idx_dummy_commands ==> adding module index";
- my $rx = "([\\\\]begin\\s*$O\\d+$C\\s*theindex[\\s\\S]*)"
- . "([\\\\]begin\\s*$O\\d+$C\\s*theindex)";
- s/$rx/\\textohtmlmoduleindex \1 \\textohtmlindex \2/o;
- # Add a button to the navigation areas:
- $CUSTOM_BUTTONS .= ('<a href="modindex.html" title="Module Index">'
- . get_my_icon('modules')
- . '</a>');
- }
- else {
- $CUSTOM_BUTTONS .= get_my_icon('blank');
- $global{'max_id'} = $id; # not sure why....
- s/([\\]begin\s*$O\d+$C\s*theindex)/\\textohtmlindex $1/o;
- s/[\\]printindex/\\textohtmlindex /o;
- }
- #----------------------------------------------------------------------
- lib_add_bbl_and_idx_dummy_commands()
- if defined(&lib_add_bbl_and_idx_dummy_commands);
-}
-
-# The bibliographic references, the appendices, the lists of figures
-# and tables etc. must appear in the contents table at the same level
-# as the outermost sectioning command. This subroutine finds what is
-# the outermost level and sets the above to the same level;
-
-sub set_depth_levels {
- # Sets $outermost_level
- my $level;
- #RRM: do not alter user-set value for $MAX_SPLIT_DEPTH
- foreach $level ("part", "chapter", "section", "subsection",
- "subsubsection", "paragraph") {
- last if (($outermost_level) = /\\($level)$delimiter_rx/);
- }
- $level = ($outermost_level ? $section_commands{$outermost_level} :
- do {$outermost_level = 'section'; 3;});
-
- #RRM: but calculate value for $MAX_SPLIT_DEPTH when a $REL_DEPTH was given
- if ($REL_DEPTH && $MAX_SPLIT_DEPTH) {
- $MAX_SPLIT_DEPTH = $level + $MAX_SPLIT_DEPTH;
- } elsif (!($MAX_SPLIT_DEPTH)) { $MAX_SPLIT_DEPTH = 1 };
-
- %unnumbered_section_commands = ('tableofcontents' => $level,
- 'listoffigures' => $level,
- 'listoftables' => $level,
- 'bibliography' => $level,
- 'textohtmlindex' => $level,
- 'textohtmlmoduleindex' => $level);
- $section_headings{'textohtmlmoduleindex'} = 'h1';
-
- %section_commands = (%unnumbered_section_commands,
- %section_commands);
-
- make_sections_rx();
-}
-
-
-# This changes the markup used for {verbatim} environments, and is the
-# best way I've found that ensures the <dl> goes on the outside of the
-# <pre>...</pre>.
-#
-# Note that this *must* be done in the init file, not the python.perl
-# style support file. The %declarations must be set before
-# initialize() is called in the main LaTeX2HTML script (which happens
-# before style files are loaded).
-#
-%declarations = ('preform' => '<dl><dd><pre class="verbatim"></pre></dl>',
- %declarations);
-
-
-# This is added to get rid of the long comment that follows the
-# doctype declaration; MSIE5 on NT4 SP4 barfs on it and drops the
-# content of the page.
-sub make_head_and_body {
- my($title, $body) = @_;
- $body = " $body" unless ($body eq '');
- my $DTDcomment = '';
- my($version, $isolanguage) = ($HTML_VERSION, 'EN');
- my %isolanguages = ( 'english', 'EN' , 'USenglish', 'EN.US'
- , 'original', 'EN' , 'german' , 'DE'
- , 'austrian', 'DE.AT', 'french' , 'FR'
- , 'spanish', 'ES');
- $isolanguage = $isolanguages{$default_language};
- $isolanguage = 'EN' unless $isolanguage;
- $title = &purify($title,1);
- eval("\$title = ". $default_title ) unless ($title);
-
- # allow user-modification of the <TITLE> tag; thanks Dan Young
- if (defined &custom_TITLE_hook) {
- $title = &custom_TITLE_hook($title, $toc_sec_title);
- }
-
- if ($DOCTYPE =~ /\/\/[\w\.]+\s*$/) { # language spec included
- $DTDcomment = "<!DOCTYPE html PUBLIC \"$DOCTYPE\">\n";
- } else {
- $DTDcomment = "<!DOCTYPE html PUBLIC \"$DOCTYPE//"
- . ($ISO_LANGUAGE ? $ISO_LANGUAGE : $isolanguage) . "\">\n";
- }
-
- $STYLESHEET = $FILE.".css" unless $STYLESHEET;
- if (!$charset && $CHARSET) { $charset = $CHARSET; $charset =~ s/_/\-/go; }
-
- join('', ($DOCTYPE ? $DTDcomment : '' )
- ,"<html>\n<head>\n<title>", $title, "</title>\n"
- , &meta_information($title)
- , ($CHARSET && $HTML_VERSION ge "2.1" ?
- "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=$charset\">\n"
- : "" )
- , ($BASE ? "<base href=\"$BASE\">\n" : "" )
- , "<link rel=\"STYLESHEET\" href=\"$STYLESHEET\">"
- , $more_links_mark
- , "\n</head>\n<body$body>");
-}
-
-1; # This must be the last line
+++ /dev/null
-# LaTeX2HTML support for the ltxmarkup package. Doesn't do indexing.
-
-package main;
-
-
-sub ltx_next_argument{
- my $param;
- $param = missing_braces()
- unless ((s/$next_pair_pr_rx/$param=$2;''/eo)
- ||(s/$next_pair_rx/$param=$2;''/eo));
- return $param;
-}
-
-
-sub do_cmd_macro{
- local($_) = @_;
- my $macro = ltx_next_argument();
- return "<tt class='macro'>\$macro</tt>" . $_;
-}
-
-sub do_cmd_env{
- local($_) = @_;
- my $env = ltx_next_argument();
- return "<tt class='environment'>\$env</tt>" . $_;
-}
-
-sub ltx_process_params{
- # Handle processing of \p and \op for parameter specifications for
- # envdesc and macrodesc. It's done this way to avoid defining do_cmd_p()
- # and do_cmd_op() functions, which would be interpreted outside the context
- # in which these commands are legal, and cause LaTeX2HTML to think they're
- # defined. This way, other uses of \p and \op are properly flagged as
- # unknown macros.
- my $s = @_[0];
- $s =~ s%\\op<<(\d+)>>(.+)<<\1>>%<tt>[</tt><var>$2</var><tt>]</tt>%;
- while ($s =~ /\\p<<(\d+)>>(.+)<<\1>>/) {
- $s =~ s%\\p<<(\d+)>>(.+)<<\1>>%<tt>{</tt><var>$2</var><tt>}</tt>%;
- }
- return $s;
-}
-
-sub do_env_macrodesc{
- local($_) = @_;
- my $macro = ltx_next_argument();
- my $params = ltx_process_params(ltx_next_argument());
- return "\n<dl class='macrodesc'>"
- . "\n<dt><b><tt class='macro'>\$macro</tt></b>"
- . "\n $params"
- . "\n<dd>"
- . $_
- . "</dl>";
-}
-
-sub do_env_envdesc{
- local($_) = @_;
- my $env = ltx_next_argument();
- my $params = ltx_process_params(ltx_next_argument());
- return "\n<dl class='envdesc'>"
- . "\n<dt><tt>\begin{<b class='environment'>$env</b>}</tt>"
- . "\n $params"
- . "\n<br /><tt>\end{<b class='environment'>$env</b>}</tt>"
- . "\n<dd>"
- . $_
- . "</dl>";
-}
-
-1; # Must end with this, because Perl is bogus.
+++ /dev/null
-# -*- perl -*-
-#
-# This implements the Python manual class. All it really needs to do it
-# load the "python" style. The style code is not moved into the class code
-# at this time, since we expect additional document class to be developed
-# for the Python documentation in the future. Appropriate relocations will
-# be made at that time.
-
-package main;
-
-do_require_package("report");
-do_require_package("alltt");
-do_require_package("python");
-
-1; # sheesh....
+++ /dev/null
-# python.perl by Fred L. Drake, Jr. <fdrake@acm.org> -*- perl -*-
-#
-# Heavily based on Guido van Rossum's myformat.perl (now obsolete).
-#
-# Extension to LaTeX2HTML for documents using myformat.sty.
-# Subroutines of the form do_cmd_<name> here define translations
-# for LaTeX commands \<name> defined in the corresponding .sty file.
-
-package main;
-
-use File::Basename;
-
-
-sub next_argument{
- my $param;
- $param = missing_braces()
- unless ((s/$next_pair_pr_rx/$param=$2;''/eo)
- ||(s/$next_pair_rx/$param=$2;''/eo));
- return $param;
-}
-
-sub next_optional_argument{
- my($param,$rx) = ('', "^\\s*(\\[([^]]*)\\])?");
- s/$rx/$param=$2;''/eo;
- return $param;
-}
-
-sub make_icon_filename($){
- my($myname, $mydir, $myext) = fileparse(@_[0], '\..*');
- chop $mydir;
- if ($mydir eq '.') {
- $mydir = $ICONSERVER;
- }
- $myext = ".$IMAGE_TYPE"
- unless $myext;
- return "$mydir$dd$myname$myext";
-}
-
-sub get_link_icon($){
- my $url = @_[0];
- if ($OFF_SITE_LINK_ICON && ($url =~ /^[-a-zA-Z0-9.]+:/)) {
- # absolute URL; assume it points off-site
- my $icon = make_icon_filename($OFF_SITE_LINK_ICON);
- return (" <img src='$icon'\n"
- . " border='0' class='offsitelink'"
- . ($OFF_SITE_LINK_ICON_HEIGHT
- ? " height='$OFF_SITE_LINK_ICON_HEIGHT'"
- : '')
- . ($OFF_SITE_LINK_ICON_WIDTH
- ? " width='$OFF_SITE_LINK_ICON_WIDTH'"
- : '')
- . " alt='[off-site link]'\n"
- . " >");
- }
- return '';
-}
-
-# This is a fairly simple hack; it supports \let when it is used to create
-# (or redefine) a macro to exactly be some other macro: \let\newname=\oldname.
-# Many possible uses of \let aren't supported or aren't supported correctly.
-#
-sub do_cmd_let{
- local($_) = @_;
- my $matched = 0;
- s/[\\]([a-zA-Z]+)\s*(=\s*)?[\\]([a-zA-Z]*)/$matched=1; ''/e;
- if ($matched) {
- my($new, $old) = ($1, $3);
- eval "sub do_cmd_$new { do_cmd_$old" . '(@_); }';
- print "\ndefining handler for \\$new using \\$old\n";
- }
- else {
- s/[\\]([a-zA-Z]+)\s*(=\s*)?([^\\])/$matched=1; ''/es;
- if ($matched) {
- my($new, $char) = ($1, $3);
- eval "sub do_cmd_$new { \"\\$char\" . \@_[0]; }";
- print "\ndefining handler for \\$new to insert '$char'\n";
- }
- else {
- write_warnings("Could not interpret \\let construct...");
- }
- }
- return $_;
-}
-
-
-# the older version of LaTeX2HTML we use doesn't support this, but we use it:
-
-sub do_cmd_textasciitilde{ '~' . @_[0]; }
-
-
-# words typeset in a special way (not in HTML though)
-
-sub do_cmd_ABC{ 'ABC' . @_[0]; }
-sub do_cmd_UNIX{ 'Unix'. @_[0]; }
-sub do_cmd_ASCII{ 'ASCII' . @_[0]; }
-sub do_cmd_POSIX{ 'POSIX' . @_[0]; }
-sub do_cmd_C{ 'C' . @_[0]; }
-sub do_cmd_Cpp{ 'C++' . @_[0]; }
-sub do_cmd_EOF{ 'EOF' . @_[0]; }
-sub do_cmd_NULL{ '<tt class="constant">NULL</tt>' . @_[0]; }
-
-sub do_cmd_e{ '\' . @_[0]; }
-
-$DEVELOPER_ADDRESS = '';
-$SHORT_VERSION = '';
-$PACKAGE_VERSION = '';
-
-sub do_cmd_version{ $PACKAGE_VERSION . @_[0]; }
-sub do_cmd_shortversion{ $SHORT_VERSION . @_[0]; }
-sub do_cmd_release{
- local($_) = @_;
- $PACKAGE_VERSION = next_argument();
- return $_;
-}
-
-sub do_cmd_setshortversion{
- local($_) = @_;
- $SHORT_VERSION = next_argument();
- return $_;
-}
-
-sub do_cmd_authoraddress{
- local($_) = @_;
- $DEVELOPER_ADDRESS = next_argument();
- return $_;
-}
-
-#sub do_cmd_developer{ do_cmd_author(@_[0]); }
-#sub do_cmd_developers{ do_cmd_author(@_[0]); }
-#sub do_cmd_developersaddress{ do_cmd_authoraddress(@_[0]); }
-
-sub do_cmd_hackscore{
- local($_) = @_;
- next_argument();
- return '_' . $_;
-}
-
-sub use_wrappers{
- local($_,$before,$after) = @_;
- my $stuff = next_argument();
- return $before . $stuff . $after . $_;
-}
-
-$IN_DESC_HANDLER = 0;
-sub do_cmd_optional{
- if ($IN_DESC_HANDLER) {
- return use_wrappers(@_[0], "</var><big>\[</big><var>",
- "</var><big>\]</big><var>");
- }
- else {
- return use_wrappers(@_[0], "<big>\[</big>", "<big>\]</big>");
- }
-}
-
-# Logical formatting (some based on texinfo), needs to be converted to
-# minimalist HTML. The "minimalist" is primarily to reduce the size of
-# output files for users that read them over the network rather than
-# from local repositories.
-
-# \file and \samp are at the end of this file since they screw up fontlock.
-
-sub do_cmd_pytype{ return @_[0]; }
-sub do_cmd_makevar{
- return use_wrappers(@_[0], '<span class="makevar">', '</span>'); }
-sub do_cmd_code{
- return use_wrappers(@_[0], '<code>', '</code>'); }
-sub do_cmd_module{
- return use_wrappers(@_[0], '<tt class="module">', '</tt>'); }
-sub do_cmd_keyword{
- return use_wrappers(@_[0], '<tt class="keyword">', '</tt>'); }
-sub do_cmd_exception{
- return use_wrappers(@_[0], '<tt class="exception">', '</tt>'); }
-sub do_cmd_class{
- return use_wrappers(@_[0], '<tt class="class">', '</tt>'); }
-sub do_cmd_function{
- return use_wrappers(@_[0], '<tt class="function">', '</tt>'); }
-sub do_cmd_constant{
- return use_wrappers(@_[0], '<tt class="constant">', '</tt>'); }
-sub do_cmd_member{
- return use_wrappers(@_[0], '<tt class="member">', '</tt>'); }
-sub do_cmd_method{
- return use_wrappers(@_[0], '<tt class="method">', '</tt>'); }
-sub do_cmd_cfunction{
- return use_wrappers(@_[0], '<tt class="cfunction">', '</tt>'); }
-sub do_cmd_cdata{
- return use_wrappers(@_[0], '<tt class="cdata">', '</tt>'); }
-sub do_cmd_ctype{
- return use_wrappers(@_[0], '<tt class="ctype">', '</tt>'); }
-sub do_cmd_regexp{
- return use_wrappers(@_[0], '<tt class="regexp">', '</tt>'); }
-sub do_cmd_character{
- return use_wrappers(@_[0], '"<tt class="character">', '</tt>"'); }
-sub do_cmd_program{
- return use_wrappers(@_[0], '<b class="program">', '</b>'); }
-sub do_cmd_programopt{
- return use_wrappers(@_[0], '<b class="programopt">', '</b>'); }
-sub do_cmd_longprogramopt{
- # note that the --- will be later converted to -- by LaTeX2HTML
- return use_wrappers(@_[0], '<b class="programopt">---', '</b>'); }
-sub do_cmd_email{
- return use_wrappers(@_[0], '<span class="email">', '</span>'); }
-sub do_cmd_mimetype{
- return use_wrappers(@_[0], '<span class="mimetype">', '</span>'); }
-sub do_cmd_var{
- return use_wrappers(@_[0], "<var>", "</var>"); }
-sub do_cmd_dfn{
- return use_wrappers(@_[0], '<i class="dfn">', '</i>'); }
-sub do_cmd_emph{
- return use_wrappers(@_[0], '<i>', '</i>'); }
-sub do_cmd_file{
- return use_wrappers(@_[0], '<span class="file">', '</span>'); }
-sub do_cmd_filenq{
- return do_cmd_file(@_[0]); }
-sub do_cmd_samp{
- return use_wrappers(@_[0], '"<tt class="samp">', '</tt>"'); }
-sub do_cmd_kbd{
- return use_wrappers(@_[0], '<kbd>', '</kbd>'); }
-sub do_cmd_strong{
- return use_wrappers(@_[0], '<b>', '</b>'); }
-sub do_cmd_textbf{
- return use_wrappers(@_[0], '<b>', '</b>'); }
-sub do_cmd_textit{
- return use_wrappers(@_[0], '<i>', '</i>'); }
-
-sub do_cmd_moreargs{
- return '...' . @_[0]; }
-sub do_cmd_unspecified{
- return '...' . @_[0]; }
-
-
-sub do_cmd_refmodule{
- # Insert the right magic to jump to the module definition.
- local($_) = @_;
- my $key = next_optional_argument();
- my $module = next_argument();
- $key = $module
- unless $key;
- return "<tt class='module'><a href='module-$key.html'>$module</a></tt>"
- . $_;
-}
-
-sub do_cmd_newsgroup{
- local($_) = @_;
- my $newsgroup = next_argument();
- my $icon = get_link_icon("news:$newsgroup");
- my $stuff = "<a class='newsgroup' href='news:$newsgroup'>"
- . "$newsgroup$icon</a>";
- return $stuff . $_;
-}
-
-sub do_cmd_envvar{
- local($_) = @_;
- my $envvar = next_argument();
- my($name,$aname,$ahref) = new_link_info();
- # The <tt> here is really to keep buildindex.py from making
- # the variable name case-insensitive.
- add_index_entry("environment variables!$envvar@<tt>$envvar</tt>",
- $ahref);
- add_index_entry("$envvar (environment variable)", $ahref);
- $aname =~ s/<a/<a class="envvar"/;
- return "$aname$envvar</a>" . $_;
-}
-
-sub do_cmd_url{
- # use the URL as both text and hyperlink
- local($_) = @_;
- my $url = next_argument();
- my $icon = get_link_icon($url);
- $url =~ s/~/~/g;
- return "<a class=\"url\" href=\"$url\">$url$icon</a>" . $_;
-}
-
-sub do_cmd_manpage{
- # two parameters: \manpage{name}{section}
- local($_) = @_;
- my $page = next_argument();
- my $section = next_argument();
- return "<span class='manpage'><i>$page</i>($section)</span>" . $_;
-}
-
-$PEP_FORMAT = "http://python.sourceforge.net/peps/pep-XXXX.html";
-$RFC_FORMAT = "http://www.ietf.org/rfc/rfcXXXX.txt";
-
-sub get_rfc_url($$){
- my($rfcnum, $format) = @_;
- $rfcnum = sprintf("%04d", $rfcnum);
- $format = "$format";
- $format =~ s/XXXX/$rfcnum/;
- return $format;
-}
-
-sub do_cmd_pep{
- local($_) = @_;
- my $rfcnumber = next_argument();
- my $id = "rfcref-" . ++$global{'max_id'};
- my $href = get_rfc_url($rfcnumber, $PEP_FORMAT);
- my $icon = get_link_icon($href);
- # Save the reference
- my $nstr = gen_index_id("Python Enhancement Proposals!PEP $rfcnumber", '');
- $index{$nstr} .= make_half_href("$CURRENT_FILE#$id");
- return ("<a class=\"rfc\" name=\"$id\"\nhref=\"$href\">PEP $rfcnumber"
- . "$icon</a>" . $_);
-}
-
-sub do_cmd_rfc{
- local($_) = @_;
- my $rfcnumber = next_argument();
- my $id = "rfcref-" . ++$global{'max_id'};
- my $href = get_rfc_url($rfcnumber, $RFC_FORMAT);
- my $icon = get_link_icon($href);
- # Save the reference
- my $nstr = gen_index_id("RFC!RFC $rfcnumber", '');
- $index{$nstr} .= make_half_href("$CURRENT_FILE#$id");
- return ("<a class=\"rfc\" name=\"$id\"\nhref=\"$href\">RFC $rfcnumber"
- . "$icon</a>" . $_);
-}
-
-sub do_cmd_citetitle{
- local($_) = @_;
- my $url = next_optional_argument();
- my $title = next_argument();
- my $icon = get_link_icon($url);
- my $repl = '';
- if ($url) {
- $repl = ("<em class='citetitle'><a\n"
- . " href='$url'\n"
- . " title='$title'\n"
- . " >$title$icon</a></em>");
- }
- else {
- $repl = "<em class='citetitle'\n >$title</em>";
- }
- return $repl . $_;
-}
-
-sub do_cmd_deprecated{
- # two parameters: \deprecated{version}{whattodo}
- local($_) = @_;
- my $release = next_argument();
- my $reason = next_argument();
- return ('<div class="versionnote">'
- . "<b>Deprecated since release $release.</b>"
- . "\n$reason</div><p>"
- . $_);
-}
-
-sub do_cmd_versionadded{
- # one parameter: \versionadded{version}
- local($_) = @_;
- my $release = next_argument();
- return ("\n<span class='versionnote'>New in version $release.</span>\n"
- . $_);
-}
-
-sub do_cmd_versionchanged{
- # one parameter: \versionchanged{version}
- local($_) = @_;
- my $explanation = next_optional_argument();
- my $release = next_argument();
- my $text = "Changed in version $release.";
- if ($explanation) {
- $text = "Changed in version $release:\n$explanation.";
- }
- return "\n<span class='versionnote'>$text</span>\n" . $_;
-}
-
-#
-# These function handle platform dependency tracking.
-#
-sub do_cmd_platform{
- local($_) = @_;
- my $platform = next_argument();
- $ModulePlatforms{"<tt class='module'>$THIS_MODULE</tt>"} = $platform;
- $platform = "Macintosh"
- if $platform eq 'Mac';
- return "\n<p class='availability'>Availability: <span"
- . "\n class='platform'>$platform</span>.</p>\n" . $_;
-}
-
-$IGNORE_PLATFORM_ANNOTATION = '';
-sub do_cmd_ignorePlatformAnnotation{
- local($_) = @_;
- $IGNORE_PLATFORM_ANNOTATION = next_argument();
- return $_;
-}
-
-
-# index commands
-
-$INDEX_SUBITEM = "";
-
-sub get_indexsubitem{
- return $INDEX_SUBITEM ? " $INDEX_SUBITEM" : '';
-}
-
-sub do_cmd_setindexsubitem{
- local($_) = @_;
- $INDEX_SUBITEM = next_argument();
- return $_;
-}
-
-sub do_cmd_withsubitem{
- # We can't really do the right thing, because LaTeX2HTML doesn't
- # do things in the right order, but we need to at least strip this stuff
- # out, and leave anything that the second argument expanded out to.
- #
- local($_) = @_;
- my $oldsubitem = $INDEX_SUBITEM;
- $INDEX_SUBITEM = next_argument();
- my $stuff = next_argument();
- my $br_id = ++$globals{'max_id'};
- my $marker = "$O$br_id$C";
- return
- $stuff
- . "\\setindexsubitem$marker$oldsubitem$marker"
- . $_;
-}
-
-# This is the prologue macro which is required to start writing the
-# mod\jobname.idx file; we can just ignore it. (Defining this suppresses
-# a warning that \makemodindex is unknown.)
-#
-sub do_cmd_makemodindex{ return @_[0]; }
-
-# We're in the document subdirectory when this happens!
-#
-open(IDXFILE, '>index.dat') || die "\n$!\n";
-open(INTLABELS, '>intlabels.pl') || die "\n$!\n";
-print INTLABELS "%internal_labels = ();\n";
-print INTLABELS "1; # hack in case there are no entries\n\n";
-
-# Using \0 for this is bad because we can't use common tools to work with the
-# resulting files. Things like grep can be useful with this stuff!
-#
-$IDXFILE_FIELD_SEP = "\1";
-
-sub write_idxfile{
- my ($ahref, $str) = @_;
- print IDXFILE $ahref, $IDXFILE_FIELD_SEP, $str, "\n";
-}
-
-
-sub gen_link{
- my($node,$target) = @_;
- print INTLABELS "\$internal_labels{\"$target\"} = \"$URL/$node\";\n";
- return "<a href='$node#$target'>";
-}
-
-sub add_index_entry{
- # add an entry to the index structures; ignore the return value
- my($str,$ahref) = @_;
- $str = gen_index_id($str, '');
- $index{$str} .= $ahref;
- write_idxfile($ahref, $str);
-}
-
-sub new_link_info{
- my $name = "l2h-" . ++$globals{'max_id'};
- my $aname = "<a name='$name'>";
- my $ahref = gen_link($CURRENT_FILE, $name);
- return ($name, $aname, $ahref);
-}
-
-$IndexMacroPattern = '';
-sub define_indexing_macro{
- my $count = @_;
- my $i = 0;
- for (; $i < $count; ++$i) {
- my $name = @_[$i];
- my $cmd = "idx_cmd_$name";
- die "\nNo function $cmd() defined!\n"
- if (!defined &$cmd);
- eval ("sub do_cmd_$name { return process_index_macros("
- . "\@_[0], '$name'); }");
- if (length($IndexMacroPattern) == 0) {
- $IndexMacroPattern = "$name";
- }
- else {
- $IndexMacroPattern .= "|$name";
- }
- }
-}
-
-$DEBUG_INDEXING = 0;
-sub process_index_macros{
- local($_) = @_;
- my $cmdname = @_[1]; # This is what triggered us in the first place;
- # we know it's real, so just process it.
- my($name,$aname,$ahref) = new_link_info();
- my $cmd = "idx_cmd_$cmdname";
- print "\nIndexing: \\$cmdname"
- if $DEBUG_INDEXING;
- &$cmd($ahref); # modifies $_ and adds index entries
- while (/^[\s\n]*\\($IndexMacroPattern)</) {
- $cmdname = "$1";
- print " \\$cmdname"
- if $DEBUG_INDEXING;
- $cmd = "idx_cmd_$cmdname";
- if (!defined &$cmd) {
- last;
- }
- else {
- s/^[\s\n]*\\$cmdname//;
- &$cmd($ahref);
- }
- }
- if (/^[ \t\r\n]/) {
- $_ = substr($_, 1);
- }
- return "$aname$anchor_invisible_mark</a>" . $_;
-}
-
-define_indexing_macro('index');
-sub idx_cmd_index{
- my $str = next_argument();
- add_index_entry("$str", @_[0]);
-}
-
-define_indexing_macro('kwindex');
-sub idx_cmd_kwindex{
- my $str = next_argument();
- add_index_entry("<tt>$str</tt>!keyword", @_[0]);
- add_index_entry("keyword!<tt>$str</tt>", @_[0]);
-}
-
-define_indexing_macro('indexii');
-sub idx_cmd_indexii{
- my $str1 = next_argument();
- my $str2 = next_argument();
- add_index_entry("$str1!$str2", @_[0]);
- add_index_entry("$str2!$str1", @_[0]);
-}
-
-define_indexing_macro('indexiii');
-sub idx_cmd_indexiii{
- my $str1 = next_argument();
- my $str2 = next_argument();
- my $str3 = next_argument();
- add_index_entry("$str1!$str2 $str3", @_[0]);
- add_index_entry("$str2!$str3, $str1", @_[0]);
- add_index_entry("$str3!$str1 $str2", @_[0]);
-}
-
-define_indexing_macro('indexiv');
-sub idx_cmd_indexiv{
- my $str1 = next_argument();
- my $str2 = next_argument();
- my $str3 = next_argument();
- my $str4 = next_argument();
- add_index_entry("$str1!$str2 $str3 $str4", @_[0]);
- add_index_entry("$str2!$str3 $str4, $str1", @_[0]);
- add_index_entry("$str3!$str4, $str1 $str2", @_[0]);
- add_index_entry("$str4!$$str1 $str2 $str3", @_[0]);
-}
-
-define_indexing_macro('ttindex');
-sub idx_cmd_ttindex{
- my $str = next_argument();
- my $entry = $str . get_indexsubitem();
- add_index_entry($entry, @_[0]);
-}
-
-sub my_typed_index_helper{
- my($word,$ahref) = @_;
- my $str = next_argument();
- add_index_entry("$str $word", $ahref);
- add_index_entry("$word!$str", $ahref);
-}
-
-define_indexing_macro('stindex', 'opindex', 'exindex', 'obindex');
-sub idx_cmd_stindex{ my_typed_index_helper('statement', @_[0]); }
-sub idx_cmd_opindex{ my_typed_index_helper('operator', @_[0]); }
-sub idx_cmd_exindex{ my_typed_index_helper('exception', @_[0]); }
-sub idx_cmd_obindex{ my_typed_index_helper('object', @_[0]); }
-
-define_indexing_macro('bifuncindex');
-sub idx_cmd_bifuncindex{
- my $str = next_argument();
- add_index_entry("<tt class='function'>$str()</tt> (built-in function)",
- @_[0]);
-}
-
-
-sub make_mod_index_entry{
- my($str,$define) = @_;
- my($name,$aname,$ahref) = new_link_info();
- # equivalent of add_index_entry() using $define instead of ''
- $ahref =~ s/\#[-_a-zA-Z0-9]*\"/\"/
- if ($define eq 'DEF');
- $str = gen_index_id($str, $define);
- $index{$str} .= $ahref;
- write_idxfile($ahref, $str);
-
- if ($define eq 'DEF') {
- # add to the module index
- $str =~ /(<tt.*<\/tt>)/;
- my $nstr = $1;
- $Modules{$nstr} .= $ahref;
- }
- return "$aname$anchor_invisible_mark2</a>";
-}
-
-
-$THIS_MODULE = '';
-$THIS_CLASS = '';
-
-sub define_module{
- my($word,$name) = @_;
- my $section_tag = join('', @curr_sec_id);
- if ($word ne "built-in" && $word ne "extension"
- && $word ne "standard" && $word ne "") {
- write_warnings("Bad module type '$word'"
- . " for \\declaremodule (module $name)");
- $word = "";
- }
- $word = "$word " if $word;
- $THIS_MODULE = "$name";
- $INDEX_SUBITEM = "(in module $name)";
- print "[$name]";
- return make_mod_index_entry(
- "<tt class='module'>$name</tt> (${word}module)", 'DEF');
-}
-
-sub my_module_index_helper{
- local($word, $_) = @_;
- my $name = next_argument();
- return define_module($word, $name) . $_;
-}
-
-sub do_cmd_modindex{ return my_module_index_helper('', @_); }
-sub do_cmd_bimodindex{ return my_module_index_helper('built-in', @_); }
-sub do_cmd_exmodindex{ return my_module_index_helper('extension', @_); }
-sub do_cmd_stmodindex{ return my_module_index_helper('standard', @_); }
-
-sub ref_module_index_helper{
- my($word, $ahref) = @_;
- my $str = next_argument();
- $word = "$word " if $word;
- $str = "<tt class='module'>$str</tt> (${word}module)";
- # can't use add_index_entry() since the 2nd arg to gen_index_id() is used;
- # just inline it all here
- $str = gen_index_id($str, 'REF');
- $index{$str} .= $ahref;
- write_idxfile($ahref, $str);
-}
-
-# these should be adjusted a bit....
-define_indexing_macro('refmodindex', 'refbimodindex',
- 'refexmodindex', 'refstmodindex');
-sub idx_cmd_refmodindex{ return ref_module_index_helper('', @_); }
-sub idx_cmd_refbimodindex{ return ref_module_index_helper('built-in', @_); }
-sub idx_cmd_refexmodindex{ return ref_module_index_helper('extension', @_); }
-sub idx_cmd_refstmodindex{ return ref_module_index_helper('standard', @_); }
-
-sub do_cmd_nodename{ return do_cmd_label(@_); }
-
-sub init_myformat{
- $anchor_invisible_mark = ' ';
- $anchor_invisible_mark2 = '';
- $anchor_mark = '';
- $icons{'anchor_mark'} = '';
-}
-init_myformat();
-
-# Create an index entry, but include the string in the target anchor
-# instead of the dummy filler.
-#
-sub make_str_index_entry{
- my($str) = @_;
- my($name,$aname,$ahref) = new_link_info();
- add_index_entry($str, $ahref);
- return "$aname$str</a>";
-}
-
-$REFCOUNTS_LOADED = 0;
-
-sub load_refcounts{
- $REFCOUNTS_LOADED = 1;
-
- my $myname, $mydir, $myext;
- ($myname, $mydir, $myext) = fileparse(__FILE__, '\..*');
- chop $mydir; # remove trailing '/'
- ($myname, $mydir, $myext) = fileparse($mydir, '\..*');
- chop $mydir; # remove trailing '/'
- $mydir = getcwd() . "$dd$mydir"
- unless $mydir =~ s|^/|/|;
- local $_;
- my $filename = "$mydir${dd}api${dd}refcounts.dat";
- open(REFCOUNT_FILE, "<$filename") || die "\n$!\n";
- print "[loading API refcount data]";
- while (<REFCOUNT_FILE>) {
- if (/([a-zA-Z0-9_]+):PyObject\*:([a-zA-Z0-9_]*):(0|[-+]1|null):(.*)$/) {
- my($func, $param, $count, $comment) = ($1, $2, $3, $4);
- #print "\n$func($param) --> $count";
- $REFCOUNTS{"$func:$param"} = $count;
- }
- }
-}
-
-sub get_refcount{
- my ($func, $param) = @_;
- load_refcounts()
- unless $REFCOUNTS_LOADED;
- return $REFCOUNTS{"$func:$param"};
-}
-
-sub do_env_cfuncdesc{
- local($_) = @_;
- my $return_type = next_argument();
- my $function_name = next_argument();
- my $arg_list = next_argument();
- my $idx = make_str_index_entry(
- "<tt class='cfunction'>$function_name()</tt>" . get_indexsubitem());
- $idx =~ s/ \(.*\)//;
- $idx =~ s/\(\)//; # ???? - why both of these?
- my $result_rc = get_refcount($function_name, '');
- my $rcinfo = '';
- if ($result_rc eq '+1') {
- $rcinfo = 'New reference';
- }
- elsif ($result_rc eq '0') {
- $rcinfo = 'Borrowed reference';
- }
- elsif ($result_rc eq 'null') {
- $rcinfo = 'Always <tt class="constant">NULL</tt>';
- }
- if ($rcinfo ne '') {
- $rcinfo = ( "\n<div class=\"refcount-info\">"
- . "\n <span class=\"label\">Return value:</span>"
- . "\n <span class=\"value\">$rcinfo.</span>"
- . "\n</div>");
- }
- return "<dl><dt>$return_type <b>$idx</b>(<var>$arg_list</var>)\n<dd>"
- . $rcinfo
- . $_
- . '</dl>';
-}
-
-sub do_env_csimplemacrodesc{
- local($_) = @_;
- my $name = next_argument();
- my $idx = make_str_index_entry("<tt class='macro'>$name</tt>");
- return "<dl><dt><b>$idx</b>\n<dd>"
- . $_
- . '</dl>'
-}
-
-sub do_env_ctypedesc{
- local($_) = @_;
- my $index_name = next_optional_argument();
- my $type_name = next_argument();
- $index_name = $type_name
- unless $index_name;
- my($name,$aname,$ahref) = new_link_info();
- add_index_entry("<tt class='ctype'>$index_name</tt> (C type)", $ahref);
- return "<dl><dt><b><tt class='ctype'>$aname$type_name</a></tt></b>\n<dd>"
- . $_
- . '</dl>'
-}
-
-sub do_env_cvardesc{
- local($_) = @_;
- my $var_type = next_argument();
- my $var_name = next_argument();
- my $idx = make_str_index_entry("<tt class='cdata'>$var_name</tt>"
- . get_indexsubitem());
- $idx =~ s/ \(.*\)//;
- return "<dl><dt>$var_type <b>$idx</b>\n"
- . '<dd>'
- . $_
- . '</dl>';
-}
-
-sub convert_args($){
- local($IN_DESC_HANDLER) = 1;
- local($_) = @_;
- return translate_commands($_);
-}
-
-sub do_env_funcdesc{
- local($_) = @_;
- my $function_name = next_argument();
- my $arg_list = convert_args(next_argument());
- my $idx = make_str_index_entry("<tt class='function'>$function_name()</tt>"
- . get_indexsubitem());
- $idx =~ s/ \(.*\)//;
- $idx =~ s/\(\)<\/tt>/<\/tt>/;
- return "<dl><dt><b>$idx</b>(<var>$arg_list</var>)\n<dd>" . $_ . '</dl>';
-}
-
-sub do_env_funcdescni{
- local($_) = @_;
- my $function_name = next_argument();
- my $arg_list = convert_args(next_argument());
- return "<dl><dt><b><tt class='function'>$function_name</tt></b>"
- . "(<var>$arg_list</var>)\n"
- . '<dd>'
- . $_
- . '</dl>';
-}
-
-sub do_cmd_funcline{
- local($_) = @_;
- my $function_name = next_argument();
- my $arg_list = convert_args(next_argument());
- my $prefix = "<tt class='function'>$function_name()</tt>";
- my $idx = make_str_index_entry($prefix . get_indexsubitem());
- $prefix =~ s/\(\)//;
-
- return "<dt><b>$prefix</b>(<var>$arg_list</var>)\n<dd>" . $_;
-}
-
-sub do_cmd_funclineni{
- local($_) = @_;
- my $function_name = next_argument();
- my $arg_list = convert_args(next_argument());
- my $prefix = "<tt class='function'>$function_name</tt>";
-
- return "<dt><b>$prefix</b>(<var>$arg_list</var>)\n<dd>" . $_;
-}
-
-# Change this flag to index the opcode entries. I don't think it's very
-# useful to index them, since they're only presented to describe the dis
-# module.
-#
-$INDEX_OPCODES = 0;
-
-sub do_env_opcodedesc{
- local($_) = @_;
- my $opcode_name = next_argument();
- my $arg_list = next_argument();
- my $idx;
- if ($INDEX_OPCODES) {
- $idx = make_str_index_entry("<tt class='opcode'>$opcode_name</tt>"
- . " (byte code instruction)");
- $idx =~ s/ \(byte code instruction\)//;
- }
- else {
- $idx = "<tt class='opcode'>$opcode_name</tt>";
- }
- my $stuff = "<dl><dt><b>$idx</b>";
- if ($arg_list) {
- $stuff .= " <var>$arg_list</var>";
- }
- return $stuff . "\n<dd>" . $_ . '</dl>';
-}
-
-sub do_env_datadesc{
- local($_) = @_;
- my $dataname = next_argument();
- my $idx = make_str_index_entry("<tt>$dataname</tt>" . get_indexsubitem());
- $idx =~ s/ \(.*\)//;
- return "<dl><dt><b>$idx</b>\n<dd>"
- . $_
- . '</dl>';
-}
-
-sub do_env_datadescni{
- local($_) = @_;
- my $idx = next_argument();
- if (! $STRING_INDEX_TT) {
- $idx = "<tt>$idx</tt>";
- }
- return "<dl><dt><b>$idx</b>\n<dd>" . $_ . '</dl>';
-}
-
-sub do_cmd_dataline{
- local($_) = @_;
- my $data_name = next_argument();
- my $idx = make_str_index_entry("<tt>$data_name</tt>" . get_indexsubitem());
- $idx =~ s/ \(.*\)//;
- return "<dt><b>$idx</b><dd>" . $_;
-}
-
-sub do_cmd_datalineni{
- local($_) = @_;
- my $data_name = next_argument();
- return "<dt><b><tt>$data_name</tt></b><dd>" . $_;
-}
-
-sub do_env_excdesc{
- local($_) = @_;
- my $excname = next_argument();
- my $idx = make_str_index_entry("<tt class='exception'>$excname</tt>");
- return "<dl><dt><b>exception $idx</b>\n<dd>" . $_ . '</dl>'
-}
-
-sub do_env_fulllineitems{ return do_env_itemize(@_); }
-
-
-sub handle_classlike_descriptor{
- local($_, $what) = @_;
- $THIS_CLASS = next_argument();
- my $arg_list = convert_args(next_argument());
- $idx = make_str_index_entry(
- "<tt class='$what'>$THIS_CLASS</tt> ($what in $THIS_MODULE)" );
- $idx =~ s/ \(.*\)//;
- return ("<dl><dt><b>$what $idx</b>(<var>$arg_list</var>)\n<dd>"
- . $_
- . '</dl>');
-}
-
-sub do_env_classdesc{
- return handle_classlike_descriptor(@_[0], "class");
-}
-
-sub do_env_excclassdesc{
- return handle_classlike_descriptor(@_[0], "exception");
-}
-
-
-sub do_env_methoddesc{
- local($_) = @_;
- my $class_name = next_optional_argument();
- $class_name = $THIS_CLASS
- unless $class_name;
- my $method = next_argument();
- my $arg_list = convert_args(next_argument());
- my $extra = '';
- if ($class_name) {
- $extra = " ($class_name method)";
- }
- my $idx = make_str_index_entry("<tt class='method'>$method()</tt>$extra");
- $idx =~ s/ \(.*\)//;
- $idx =~ s/\(\)//;
- return "<dl><dt><b>$idx</b>(<var>$arg_list</var>)\n<dd>" . $_ . '</dl>';
-}
-
-
-sub do_cmd_methodline{
- local($_) = @_;
- my $class_name = next_optional_argument();
- $class_name = $THIS_CLASS
- unless $class_name;
- my $method = next_argument();
- my $arg_list = convert_args(next_argument());
- my $extra = '';
- if ($class_name) {
- $extra = " ($class_name method)";
- }
- my $idx = make_str_index_entry("<tt class='method'>$method()</tt>$extra");
- $idx =~ s/ \(.*\)//;
- $idx =~ s/\(\)//;
- return "<dt><b>$idx</b>(<var>$arg_list</var>)\n<dd>"
- . $_;
-}
-
-
-sub do_cmd_methodlineni{
- local($_) = @_;
- next_optional_argument();
- my $method = next_argument();
- my $arg_list = convert_args(next_argument());
- return "<dt><b>$method</b>(<var>$arg_list</var>)\n<dd>"
- . $_;
-}
-
-sub do_env_methoddescni{
- local($_) = @_;
- next_optional_argument();
- my $method = next_argument();
- my $arg_list = convert_args(next_argument());
- return "<dl><dt><b>$method</b>(<var>$arg_list</var>)\n<dd>"
- . $_
- . '</dl>';
-}
-
-
-sub do_env_memberdesc{
- local($_) = @_;
- my $class = next_optional_argument();
- my $member = next_argument();
- $class = $THIS_CLASS
- unless $class;
- my $extra = '';
- $extra = " ($class attribute)"
- if ($class ne '');
- my $idx = make_str_index_entry("<tt class='member'>$member</tt>$extra");
- $idx =~ s/ \(.*\)//;
- $idx =~ s/\(\)//;
- return "<dl><dt><b>$idx</b>\n<dd>" . $_ . '</dl>';
-}
-
-
-sub do_cmd_memberline{
- local($_) = @_;
- my $class = next_optional_argument();
- my $member = next_argument();
- $class = $THIS_CLASS
- unless $class;
- my $extra = '';
- $extra = " ($class attribute)"
- if ($class ne '');
- my $idx = make_str_index_entry("<tt class='member'>$member</tt>$extra");
- $idx =~ s/ \(.*\)//;
- $idx =~ s/\(\)//;
- return "<dt><b>$idx</b><dd>" . $_;
-}
-
-sub do_env_memberdescni{
- local($_) = @_;
- next_optional_argument();
- my $member = next_argument();
- return "<dl><dt><b><tt class='member'>$member</tt></b>\n<dd>"
- . $_
- . '</dl>';
-}
-
-
-sub do_cmd_memberlineni{
- local($_) = @_;
- next_optional_argument();
- my $member = next_argument();
- return "<dt><b><tt class='member'>$member</tt></b><dd>" . $_;
-}
-
-@col_aligns = ('<td>', '<td>', '<td>', '<td>');
-
-sub fix_font{
- # do a little magic on a font name to get the right behavior in the first
- # column of the output table
- my $font = @_[0];
- if ($font eq 'textrm') {
- $font = '';
- }
- elsif ($font eq 'file' || $font eq 'filenq') {
- $font = 'tt class="file"';
- }
- elsif ($font eq 'member') {
- $font = 'tt class="member"';
- }
- elsif ($font eq 'class') {
- $font = 'tt class="class"';
- }
- elsif ($font eq 'constant') {
- $font = 'tt class="constant"';
- }
- elsif ($font eq 'kbd') {
- $font = 'kbd';
- }
- elsif ($font eq 'programopt') {
- $font = 'b';
- }
- elsif ($font eq 'exception') {
- $font = 'tt class="exception"';
- }
- return $font;
-}
-
-sub figure_column_alignment{
- my $a = @_[0];
- my $mark = substr($a, 0, 1);
- my $r = '';
- if ($mark eq 'c')
- { $r = ' align="center"'; }
- elsif ($mark eq 'r')
- { $r = ' align="right"'; }
- elsif ($mark eq 'l')
- { $r = ' align="left"'; }
- elsif ($mark eq 'p')
- { $r = ' align="left"'; }
- return $r;
-}
-
-sub setup_column_alignments{
- local($_) = @_;
- my($s1,$s2,$s3,$s4) = split(/[|]/,$_);
- my $a1 = figure_column_alignment($s1);
- my $a2 = figure_column_alignment($s2);
- my $a3 = figure_column_alignment($s3);
- my $a4 = figure_column_alignment($s4);
- $col_aligns[0] = "<td$a1 valign=\"baseline\">";
- $col_aligns[1] = "<td$a2>";
- $col_aligns[2] = "<td$a3>";
- $col_aligns[3] = "<td$a4>";
- # return the aligned header start tags
- return ("<th$a1>", "<th$a2>", "<th$a3>", "<th$a4>");
-}
-
-sub get_table_col1_fonts{
- my $font = $globals{'lineifont'};
- my ($sfont,$efont) = ('', '');
- if ($font) {
- $sfont = "<$font>";
- $efont = "</$font>";
- $efont =~ s/ .*>/>/;
- }
- return ($sfont, $efont);
-}
-
-sub do_env_tableii{
- local($_) = @_;
- my($th1,$th2,$th3,$th4) = setup_column_alignments(next_argument());
- my $font = fix_font(next_argument());
- my $h1 = next_argument();
- my $h2 = next_argument();
- s/[\s\n]+//;
- $globals{'lineifont'} = $font;
- my $a1 = $col_aligns[0];
- my $a2 = $col_aligns[1];
- s/\\lineii</\\lineii[$a1|$a2]</g;
- return '<table border align="center" style="border-collapse: collapse">'
- . "\n <thead>"
- . "\n <tr class=\"tableheader\">"
- . "\n $th1<b>$h1</b>\ </th>"
- . "\n $th2<b>$h2</b>\ </th>"
- . "\n </tr>"
- . "\n </thead>"
- . "\n <tbody valign='baseline'>"
- . $_
- . "\n </tbody>"
- . "\n</table>";
-}
-
-sub do_env_longtableii{
- return do_env_tableii(@_);
-}
-
-sub do_cmd_lineii{
- local($_) = @_;
- my $aligns = next_optional_argument();
- my $c1 = next_argument();
- my $c2 = next_argument();
- s/[\s\n]+//;
- my($sfont,$efont) = get_table_col1_fonts();
- $c2 = ' ' if ($c2 eq '');
- my($c1align,$c2align) = split('\|', $aligns);
- my $padding = '';
- if ($c1align =~ /align="right"/ || $c1 eq '') {
- $padding = ' ';
- }
- return "\n <tr>$c1align$sfont$c1$efont$padding</td>\n"
- . " $c2align$c2</td>"
- . $_;
-}
-
-sub do_env_tableiii{
- local($_) = @_;
- my($th1,$th2,$th3,$th4) = setup_column_alignments(next_argument());
- my $font = fix_font(next_argument());
- my $h1 = next_argument();
- my $h2 = next_argument();
- my $h3 = next_argument();
- s/[\s\n]+//;
- $globals{'lineifont'} = $font;
- my $a1 = $col_aligns[0];
- my $a2 = $col_aligns[1];
- my $a3 = $col_aligns[2];
- s/\\lineiii</\\lineiii[$a1|$a2|$a3]</g;
- return '<table border align="center" style="border-collapse: collapse">'
- . "\n <thead>"
- . "\n <tr class=\"tableheader\">"
- . "\n $th1<b>$h1</b>\ </th>"
- . "\n $th2<b>$h2</b>\ </th>"
- . "\n $th3<b>$h3</b>\ </th>"
- . "\n </tr>"
- . "\n </thead>"
- . "\n <tbody valign='baseline'>"
- . $_
- . "\n </tbody>"
- . "\n</table>";
-}
-
-sub do_env_longtableiii{
- return do_env_tableiii(@_);
-}
-
-sub do_cmd_lineiii{
- local($_) = @_;
- my $aligns = next_optional_argument();
- my $c1 = next_argument();
- my $c2 = next_argument();
- my $c3 = next_argument();
- s/[\s\n]+//;
- my($sfont,$efont) = get_table_col1_fonts();
- $c3 = ' ' if ($c3 eq '');
- my($c1align,$c2align,$c3align) = split('\|', $aligns);
- my $padding = '';
- if ($c1align =~ /align="right"/ || $c1 eq '') {
- $padding = ' ';
- }
- return "\n <tr>$c1align$sfont$c1$efont$padding</td>\n"
- . " $c2align$c2</td>\n"
- . " $c3align$c3</td>"
- . $_;
-}
-
-sub do_env_tableiv{
- local($_) = @_;
- my($th1,$th2,$th3,$th4) = setup_column_alignments(next_argument());
- my $font = fix_font(next_argument());
- my $h1 = next_argument();
- my $h2 = next_argument();
- my $h3 = next_argument();
- my $h4 = next_argument();
- s/[\s\n]+//;
- $globals{'lineifont'} = $font;
- my $a1 = $col_aligns[0];
- my $a2 = $col_aligns[1];
- my $a3 = $col_aligns[2];
- my $a4 = $col_aligns[3];
- s/\\lineiv</\\lineiv[$a1|$a2|$a3|$a4]</g;
- return '<table border align="center" style="border-collapse: collapse">'
- . "\n <thead>"
- . "\n <tr class=\"tableheader\">"
- . "\n $th1<b>$h1</b>\ </th>"
- . "\n $th2<b>$h2</b>\ </th>"
- . "\n $th3<b>$h3</b>\ </th>"
- . "\n $th4<b>$h4</b>\ </th>"
- . "\n </tr>"
- . "\n </thead>"
- . "\n <tbody valign='baseline'>"
- . $_
- . "\n </tbody>"
- . "\n</table>";
-}
-
-sub do_env_longtableiv{
- return do_env_tableiv(@_);
-}
-
-sub do_cmd_lineiv{
- local($_) = @_;
- my $aligns = next_optional_argument();
- my $c1 = next_argument();
- my $c2 = next_argument();
- my $c3 = next_argument();
- my $c4 = next_argument();
- s/[\s\n]+//;
- my($sfont,$efont) = get_table_col1_fonts();
- $c4 = ' ' if ($c4 eq '');
- my($c1align,$c2align,$c3align,$c4align) = split('\|', $aligns);
- my $padding = '';
- if ($c1align =~ /align="right"/ || $c1 eq '') {
- $padding = ' ';
- }
- return "\n <tr>$c1align$sfont$c1$efont$padding</td>\n"
- . " $c2align$c2</td>\n"
- . " $c3align$c3</td>\n"
- . " $c4align$c4</td>"
- . $_;
-}
-
-
-# These can be used to control the title page appearance;
-# they need a little bit of documentation.
-#
-# If $TITLE_PAGE_GRAPHIC is set, it should be the name of a file in the
-# $ICONSERVER directory, or include path information (other than "./"). The
-# default image type will be assumed if an extension is not provided.
-#
-# If specified, the "title page" will contain two colums: one containing the
-# title/author/etc., and the other containing the graphic. Use the other
-# four variables listed here to control specific details of the layout; all
-# are optional.
-#
-# $TITLE_PAGE_GRAPHIC = "my-company-logo";
-# $TITLE_PAGE_GRAPHIC_COLWIDTH = "30%";
-# $TITLE_PAGE_GRAPHIC_WIDTH = 150;
-# $TITLE_PAGE_GRAPHIC_HEIGHT = 150;
-# $TITLE_PAGE_GRAPHIC_ON_RIGHT = 0;
-
-sub make_my_titlepage() {
- my $the_title = "";
- if ($t_title) {
- $the_title .= "\n<h1>$t_title</h1>";
- }
- else {
- write_warnings("\nThis document has no title.");
- }
- if ($t_author) {
- if ($t_authorURL) {
- my $href = translate_commands($t_authorURL);
- $href = make_named_href('author', $href,
- "<b><font size='+2'>$t_author</font></b>");
- $the_title .= "\n<p>$href</p>";
- }
- else {
- $the_title .= ("\n<p><b><font size='+2'>$t_author</font></b></p>");
- }
- }
- else {
- write_warnings("\nThere is no author for this document.");
- }
- if ($t_institute) {
- $the_title .= "\n<p>$t_institute</p>";
- }
- if ($DEVELOPER_ADDRESS) {
- $the_title .= "\n<p>$DEVELOPER_ADDRESS</p>";
- }
- if ($t_affil) {
- $the_title .= "\n<p><i>$t_affil</i></p>";
- }
- if ($t_date) {
- $the_title .= "\n<p>";
- if ($PACKAGE_VERSION) {
- $the_title .= "<strong>Release $PACKAGE_VERSION</strong><br>\n";
- }
- $the_title .= "<strong>$t_date</strong></p>"
- }
- if ($t_address) {
- $the_title .= "\n<p>$t_address</p>";
- }
- else {
- $the_title .= "\n<p>";
- }
- if ($t_email) {
- $the_title .= "\n<p>$t_email</p>";
- }
- return $the_title;
-}
-
-sub make_my_titlegraphic() {
- my $filename = make_icon_filename($TITLE_PAGE_GRAPHIC);
- my $graphic = "<td class=\"titlegraphic\"";
- $graphic .= " width=\"$TITLE_PAGE_GRAPHIC_COLWIDTH\""
- if ($TITLE_PAGE_GRAPHIC_COLWIDTH);
- $graphic .= "><img";
- $graphic .= " width=\"$TITLE_PAGE_GRAPHIC_WIDTH\""
- if ($TITLE_PAGE_GRAPHIC_WIDTH);
- $graphic .= " height=\"$TITLE_PAGE_GRAPHIC_HEIGHT\""
- if ($TITLE_PAGE_GRAPHIC_HEIGHT);
- $graphic .= "\n src=\"$filename\"></td>\n";
- return $graphic;
-}
-
-sub do_cmd_maketitle {
- local($_) = @_;
- my $the_title = "\n<div class=\"titlepage\">";
- if ($TITLE_PAGE_GRAPHIC) {
- if ($TITLE_PAGE_GRAPHIC_ON_RIGHT) {
- $the_title .= ("\n<table border=\"0\" width=\"100%\">"
- . "<tr align=\"right\">\n<td>"
- . make_my_titlepage()
- . "</td>\n"
- . make_my_titlegraphic()
- . "</tr>\n</table>");
- }
- else {
- $the_title .= ("\n<table border=\"0\" width=\"100%\"><tr>\n"
- . make_my_titlegraphic()
- . "<td>"
- . make_my_titlepage()
- . "</td></tr>\n</table>");
- }
- }
- else {
- $the_title .= ("\n<center>"
- . make_my_titlepage()
- . "\n</center>");
- }
- $the_title .= "\n</div>";
- return $the_title . $_;
- $the_title .= "\n</center></div>";
- return $the_title . $_ ;
-}
-
-
-#
-# Module synopsis support
-#
-
-require SynopsisTable;
-
-sub get_chapter_id(){
- my $id = do_cmd_thechapter('');
- $id =~ s/<SPAN CLASS="arabic">(\d+)<\/SPAN>/\1/;
- $id =~ s/\.//;
- return $id;
-}
-
-# 'chapter' => 'SynopsisTable instance'
-%ModuleSynopses = ();
-
-sub get_synopsis_table($){
- my($chap) = @_;
- my $key;
- foreach $key (keys %ModuleSynopses) {
- if ($key eq $chap) {
- return $ModuleSynopses{$chap};
- }
- }
- my $st = SynopsisTable->new();
- $ModuleSynopses{$chap} = $st;
- return $st;
-}
-
-sub do_cmd_moduleauthor{
- local($_) = @_;
- next_argument();
- next_argument();
- return $_;
-}
-
-sub do_cmd_sectionauthor{
- local($_) = @_;
- next_argument();
- next_argument();
- return $_;
-}
-
-sub do_cmd_declaremodule{
- local($_) = @_;
- my $key = next_optional_argument();
- my $type = next_argument();
- my $name = next_argument();
- my $st = get_synopsis_table(get_chapter_id());
- #
- $key = $name unless $key;
- $type = 'built-in' if $type eq 'builtin';
- $st->declare($name, $key, $type);
- define_module($type, $name);
- return anchor_label("module-$key",$CURRENT_FILE,$_)
-}
-
-sub do_cmd_modulesynopsis{
- local($_) = @_;
- my $st = get_synopsis_table(get_chapter_id());
- $st->set_synopsis($THIS_MODULE, translate_commands(next_argument()));
- return $_;
-}
-
-sub do_cmd_localmoduletable{
- local($_) = @_;
- my $chap = get_chapter_id();
- my $st = get_synopsis_table($chap);
- $st->set_file("$CURRENT_FILE");
- return "<tex2html-localmoduletable><$chap>\\tableofchildlinks[off]" . $_;
-}
-
-sub process_all_localmoduletables{
- my $key;
- my $st, $file;
- foreach $key (keys %ModuleSynopses) {
- $st = $ModuleSynopses{$key};
- $file = $st->get_file();
- if ($file) {
- process_localmoduletables_in_file($file);
- }
- else {
- print "\nsynopsis table $key has no file association";
- }
- }
-}
-
-sub process_localmoduletables_in_file{
- my $file = @_[0];
- open(MYFILE, "<$file");
- local($_);
- sysread(MYFILE, $_, 1024*1024);
- close(MYFILE);
- # need to get contents of file in $_
- while (/<tex2html-localmoduletable><(\d+)>/) {
- my $match = $&;
- my $chap = $1;
- my $st = get_synopsis_table($chap);
- my $data = $st->tohtml();
- s/$match/$data/;
- }
- open(MYFILE,">$file");
- print MYFILE $_;
- close(MYFILE);
-}
-sub process_python_state{
- process_all_localmoduletables();
-}
-
-
-#
-# "See also:" -- references placed at the end of a \section
-#
-
-sub do_env_seealso{
- return "<div class='seealso'>\n "
- . "<p class='heading'><b>See Also:</b></p>\n"
- . @_[0]
- . '</div>';
-}
-
-sub do_cmd_seemodule{
- # Insert the right magic to jump to the module definition. This should
- # work most of the time, at least for repeat builds....
- local($_) = @_;
- my $key = next_optional_argument();
- my $module = next_argument();
- my $text = next_argument();
- my $period = '.';
- $key = $module
- unless $key;
- if ($text =~ /\.$/) {
- $period = '';
- }
- return '<dl compact class="seemodule">'
- . "\n <dt>Module <b><tt class='module'><a href='module-$key.html'>"
- . "$module</a></tt>:</b>"
- . "\n <dd>$text$period\n </dl>"
- . $_;
-}
-
-sub strip_html_markup($){
- my $str = @_[0];
- my $s = "$str";
- $s =~ s/<[a-zA-Z0-9]+(\s+[a-zA-Z0-9]+(\s*=\s*(\'[^\']*\'|\"[^\"]*\"|[a-zA-Z0-9]+))?)*\s*>//g;
- $s =~ s/<\/[a-zA-Z0-9]+>//g;
- return $s;
-}
-
-sub handle_rfclike_reference{
- local($_, $what, $format) = @_;
- my $rfcnum = next_argument();
- my $title = next_argument();
- my $text = next_argument();
- my $url = get_rfc_url($rfcnum, $format);
- my $icon = get_link_icon($url);
- my $attrtitle = strip_html_markup($title);
- return '<dl compact class="seerfc">'
- . "\n <dt><a href=\"$url\""
- . "\n title=\"$attrtitle\""
- . "\n >$what $rfcnum, <em>$title</em>$icon</a>"
- . "\n <dd>$text\n </dl>"
- . $_;
-}
-
-sub do_cmd_seepep{
- return handle_rfclike_reference(@_[0], "PEP", $PEP_FORMAT);
-}
-
-sub do_cmd_seerfc{
- return handle_rfclike_reference(@_[0], "RFC", $RFC_FORMAT);
-}
-
-sub do_cmd_seetitle{
- local($_) = @_;
- my $url = next_optional_argument();
- my $title = next_argument();
- my $text = next_argument();
- if ($url) {
- my $icon = get_link_icon($url);
- return '<dl compact class="seetitle">'
- . "\n <dt><em class=\"citetitle\"><a href=\"$url\""
- . "\n >$title$icon</a></em>"
- . "\n <dd>$text\n </dl>"
- . $_;
- }
- return '<dl compact class="seetitle">'
- . "\n <dt><em class=\"citetitle\""
- . "\n >$title</em>"
- . "\n <dd>$text\n </dl>"
- . $_;
-}
-
-sub do_cmd_seeurl{
- local($_) = @_;
- my $url = next_argument();
- my $text = next_argument();
- my $icon = get_link_icon($url);
- return '<dl compact class="seeurl">'
- . "\n <dt><a href=\"$url\""
- . "\n class=\"url\">$url$icon</a>"
- . "\n <dd>$text\n </dl>"
- . $_;
-}
-
-sub do_cmd_seetext{
- local($_) = @_;
- my $content = next_argument();
- return '<div class="seetext"><p>' . $content . '</div>' . $_;
-}
-
-
-#
-# Definition list support.
-#
-
-sub do_env_definitions{
- return "<dl class='definitions'>" . @_[0] . "</dl>\n";
-}
-
-sub do_cmd_term{
- local($_) = @_;
- my $term = next_argument();
- my($name,$aname,$ahref) = new_link_info();
- # could easily add an index entry here...
- return "<dt><b>$aname" . $term . "</a></b>\n<dd>" . $_;
-}
-
-
-# I don't recall exactly why this was needed, but it was very much needed.
-# We'll see if anything breaks when I move the "code" line out -- some
-# things broke with it in.
-
-#code # {}
-process_commands_wrap_deferred(<<_RAW_ARG_DEFERRED_CMDS_);
-declaremodule # [] # {} # {}
-memberline # [] # {}
-methodline # [] # {} # {}
-modulesynopsis # {}
-platform # {}
-samp # {}
-setindexsubitem # {}
-withsubitem # {} # {}
-_RAW_ARG_DEFERRED_CMDS_
-
-
-$alltt_start = '<dl><dd><pre class="verbatim">';
-$alltt_end = '</pre></dl>';
-
-sub do_env_alltt {
- local ($_) = @_;
- local($closures,$reopens,@open_block_tags);
-
- # get the tag-strings for all open tags
- local(@keep_open_tags) = @$open_tags_R;
- ($closures,$reopens) = &preserve_open_tags() if (@$open_tags_R);
-
- # get the tags for text-level tags only
- $open_tags_R = [ @keep_open_tags ];
- local($local_closures, $local_reopens);
- ($local_closures, $local_reopens,@open_block_tags)
- = &preserve_open_block_tags
- if (@$open_tags_R);
-
- $open_tags_R = [ @open_block_tags ];
-
- do {
- local($open_tags_R) = [ @open_block_tags ];
- local(@save_open_tags) = ();
-
- local($cnt) = ++$global{'max_id'};
- $_ = join('',"$O$cnt$C\\tt$O", ++$global{'max_id'}, $C
- , $_ , $O, $global{'max_id'}, "$C$O$cnt$C");
-
- $_ = &translate_environments($_);
- $_ = &translate_commands($_) if (/\\/);
-
- # preserve space-runs, using
- while (s/(\S) ( +)/$1$2;SPMnbsp;/g){};
- s/(<BR>) /$1;SPMnbsp;/g;
-
- $_ = join('', $closures, $alltt_start, $local_reopens
- , $_
- , &balance_tags() #, $local_closures
- , $alltt_end, $reopens);
- undef $open_tags_R; undef @save_open_tags;
- };
- $open_tags_R = [ @keep_open_tags ];
- $_;
-}
-
-
-1; # This must be the last line
+++ /dev/null
-#! /bin/sh
-
-# Script to push docs from my development area to SourceForge, where the
-# update-docs.sh script unpacks them into their final destination.
-
-TARGET=python.sourceforge.net:/home/users/fdrake/tmp
-
-ADDRESSES='python-dev@python.org doc-sig@python.org python-list@python.org'
-
-EXPLANATION=''
-
-if [ "$1" = '-m' ] ; then
- EXPLANATION="$2"
- shift 2
-elif [ "$1" ] ; then
- EXPLANATION="`cat $1`"
- shift 1
-fi
-
-START="`pwd`"
-MYDIR="`dirname $0`"
-cd "$MYDIR"
-MYDIR="`pwd`"
-
-cd ..
-
-# now in .../Doc/
-make --no-print-directory || exit $?
-make --no-print-directory bziphtml || exit $?
-RELEASE=`grep '^RELEASE=' Makefile | sed 's|RELEASE=||'`
-PACKAGE="html-$RELEASE.tar.bz2"
-scp "$PACKAGE" tools/update-docs.sh $TARGET/ || exit $?
-ssh python.sourceforge.net tmp/update-docs.sh $PACKAGE '&&' rm tmp/update-docs.sh || exit $?
-
-Mail -s '[development doc updates]' $ADDRESSES <<EOF
-The development version of the documentation has been updated:
-
- http://python.sourceforge.net/devel-docs/
-
-$EXPLANATION
-EOF
-exit $?
+++ /dev/null
-"""Support functions for loading the reference count data file."""
-__version__ = '$Revision: 1.1.1.1 $'
-
-import os
-import string
-import sys
-
-
-# Determine the expected location of the reference count file:
-try:
- p = os.path.dirname(__file__)
-except NameError:
- p = sys.path[0]
-p = os.path.normpath(os.path.join(os.getcwd(), p, os.pardir,
- "api", "refcounts.dat"))
-DEFAULT_PATH = p
-del p
-
-
-def load(path=DEFAULT_PATH):
- return loadfile(open(path))
-
-
-def loadfile(fp):
- d = {}
- while 1:
- line = fp.readline()
- if not line:
- break
- line = string.strip(line)
- if line[:1] in ("", "#"):
- # blank lines and comments
- continue
- parts = string.split(line, ":", 4)
- function, type, arg, refcount, comment = parts
- if refcount == "null":
- refcount = None
- elif refcount:
- refcount = int(refcount)
- else:
- refcount = None
- #
- # Get the entry, creating it if needed:
- #
- try:
- entry = d[function]
- except KeyError:
- entry = d[function] = Entry(function)
- #
- # Update the entry with the new parameter or the result information.
- #
- if arg:
- entry.args.append((arg, type, refcount))
- else:
- entry.result_type = type
- entry.result_refs = refcount
- return d
-
-
-class Entry:
- def __init__(self, name):
- self.name = name
- self.args = []
- self.result_type = ''
- self.result_refs = None
-
-
-def dump(d):
- """Dump the data in the 'canonical' format, with functions in
- sorted order."""
- items = d.items()
- items.sort()
- first = 1
- for k, entry in items:
- if first:
- first = 0
- else:
- print
- s = entry.name + ":%s:%s:%s:"
- if entry.result_refs is None:
- r = ""
- else:
- r = entry.result_refs
- print s % (entry.result_type, "", r)
- for t, n, r in entry.args:
- if r is None:
- r = ""
- print s % (t, n, r)
-
-
-def main():
- d = load()
- dump(d)
-
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-# Simple makefile to control XML generation for the entire document tree.
-# This should be used from the top-level directory (Doc/), not the directory
-# that actually contains this file:
-#
-# $ pwd
-# .../Doc
-# $ make -f tools/sgmlconv/Makefile
-
-TOPDIR=.
-TOOLSDIR=tools
-
-SGMLRULES=../$(TOOLSDIR)/sgmlconv/make.rules
-# The 'inst' directory breaks the conversion, so skip it for now.
-SUBDIRS=api dist ext lib mac ref tut
-SUBMAKE=$(MAKE) -f $(SGMLRULES) TOOLSDIR=../$(TOOLSDIR)
-
-all: xml
-
-.PHONY: esis xml
-.PHONY: $(SUBDIRS)
-
-xml:
- for DIR in $(SUBDIRS) ; do \
- (cd $$DIR; $(SUBMAKE) xml) || exit $$? ; done
-
-esis:
- for DIR in $(SUBDIRS) ; do \
- (cd $$DIR; $(SUBMAKE) esis) || exit $$? ; done
-
-esis1:
- for DIR in $(SUBDIRS) ; do \
- (cd $$DIR; $(SUBMAKE) esis1) || exit $$? ; done
-
-tarball: xml
- tar cf - tools/sgmlconv */*.xml | gzip -9 >xml-1.5.2b2.tgz
-
-api:
- cd api; $(SUBMAKE)
-
-dist:
- cd dist; $(SUBMAKE)
-
-ext:
- cd ext; $(SUBMAKE)
-
-inst:
- cd inst; $(SUBMAKE)
-
-lib:
- cd lib; $(SUBMAKE)
-
-mac:
- cd mac; $(SUBMAKE)
-
-ref:
- cd ref; $(SUBMAKE)
-
-tut:
- cd tut; $(SUBMAKE)
-
-clean:
- for DIR in $(SUBDIRS) ; do \
- (cd $$DIR; $(SUBMAKE) clean) ; done
-
-clobber:
- for DIR in $(SUBDIRS) ; do \
- (cd $$DIR; $(SUBMAKE) clobber) ; done
+++ /dev/null
-These scripts and Makefile fragment are used to convert the Python
-documentation in LaTeX format to XML.
-
-This material is preliminary and incomplete. Python 2.0 is required.
-
-To convert all documents to XML:
-
- cd Doc/
- make -f tools/sgmlconv/Makefile
-
-To convert one document to XML:
-
- cd Doc/<document-dir>
- make -f ../tools/sgmlconv/make.rules TOOLSDIR=../tools
-
-Please send comments and bug reports to python-docs@python.org.
-
-
-What do the tools do?
----------------------
-
-latex2esis.py
- Reads in a conversion specification written in XML
- (conversion.xml), reads a LaTeX document fragment, and interprets
- the markup according to the specification. The output is a stream
- of ESIS events like those created by the nsgmls SGML parser, but
- is *not* guaranteed to represent a single tree! This is done to
- allow conversion per entity rather than per document. Since many
- of the LaTeX files for the Python documentation contain two
- sections on closely related modules, it is important to allow both
- of the resulting <section> elements to exist in the same output
- stream. Additionally, since comments are not supported in ESIS,
- comments are converted to <COMMENT> elements, which might exist at
- the same level as the top-level content elements.
-
- The output of latex2esis.py gets saved as <filename>.esis1.
-
-docfixer.py
- This is the really painful part of the conversion. Well, it's the
- second really painful part, but more of the pain is specific to
- the structure of the Python documentation and desired output
- rather than to the parsing of LaTeX markup.
-
- This script loads the ESIS data created by latex2esis.py into a
- DOM document *fragment* (remember, the latex2esis.py output may
- not be well-formed). Once loaded, it walks over the tree many
- times looking for a variety of possible specific
- micro-conversions. Most of the code is not in any way "general".
- After processing the fragment, a new ESIS data stream is written
- out. Like the input, it may not represent a well-formed
- document, but does represent a parsed entity.
-
- The output of docfixer.py is what gets saved in <filename>.esis.
-
-esis2sgml.py
- Reads an ESIS stream and convert to SGML or XML. This also
- converts <COMMENT> elements to real comments. This works quickly
- because there's not much to actually do.
+++ /dev/null
-<?xml version="1.0" encoding="iso-8859-1"?>
-<conversion>
- <!-- Miscellaneous. -->
- <macro name="declaremodule">
- <attribute name="id" optional="yes"/>
- <attribute name="type"/>
- <attribute name="name"/>
- </macro>
- <macro name="modulesynopsis">
- <content/>
- </macro>
- <macro name="platform">
- <content/>
- </macro>
- <macro name="deprecated">
- <attribute name="version"/>
- <content/>
- </macro>
- <macro name="label">
- <attribute name="id"/>
- </macro>
- <macro name="nodename" outputname="label">
- <attribute name="id"/>
- </macro>
- <macro name="localmoduletable"/>
- <macro name="manpage">
- <attribute name="name"/>
- <attribute name="section"/>
- </macro>
- <macro name="module">
- <content/>
- </macro>
- <macro name="moduleauthor">
- <attribute name="name"/>
- <attribute name="email"/>
- </macro>
- <macro name="citetitle">
- <attribute name="href" optional="yes"/>
- <content/>
- </macro>
- <macro name="rfc">
- <attribute name="num"/>
- </macro>
- <macro name="sectionauthor" outputname="author">
- <attribute name="name"/>
- <attribute name="email"/>
- </macro>
- <macro name="author">
- <attribute name="name"/>
- </macro>
- <macro name="authoraddress">
- <content/>
- </macro>
- <macro name="shortversion"/>
- <macro name="versionadded">
- <attribute name="version"/>
- </macro>
- <!-- This is broken: we need to re-order the optional and required
- parameters, making the optional parameter the content for the
- element. The processor is not powerful enough to handle this.
- -->
- <macro name="versionchanged">
- <attribute name="how" optional="yes"/>
- <attribute name="version"/>
- </macro>
-
- <!-- Module referencing. -->
- <macro name="refmodule" outputname="module">
- <attribute name="" optional="yes"/>
- <attribute name="link">yes</attribute>
- <content/>
- </macro>
-
- <!-- Information units. -->
- <!-- C things. -->
- <environment name="cfuncdesc">
- <attribute name="type"/>
- <attribute name="name"/>
- <child name="args"/>
- </environment>
- <environment name="ctypedesc">
- <attribute name="tag" optional="yes"/>
- <attribute name="name"/>
- </environment>
- <environment name="cvardesc">
- <attribute name="type"/>
- <attribute name="name"/>
- </environment>
-
- <!-- Python things. -->
- <macro name="optional">
- <content/>
- </macro>
- <macro name="unspecified"/>
- <macro name="moreargs"/>
- <environment name="classdesc">
- <attribute name="name"/>
- <child name="args"/>
- </environment>
- <environment name="datadesc">
- <attribute name="name"/>
- </environment>
- <macro name="dataline">
- <attribute name="name"/>
- </macro>
- <environment name="excdesc">
- <attribute name="name"/>
- </environment>
-
- <environment name="funcdesc">
- <attribute name="name"/>
- <child name="args"/>
- </environment>
- <macro name="funcline">
- <attribute name="name"/>
- <child name="args"/>
- </macro>
- <environment name="funcdescni" outputname="funcdesc">
- <attribute name="index">no</attribute>
- <attribute name="name"/>
- <child name="args"/>
- </environment>
- <macro name="funclineni" outputname="funcline">
- <attribute name="index">no</attribute>
- <attribute name="name"/>
- <child name="args"/>
- </macro>
-
- <environment name="memberdesc">
- <attribute name="class" optional="yes"/>
- <attribute name="name"/>
- </environment>
- <environment name="memberdescni" outputname="memberdesc">
- <attribute name="index">no</attribute>
- <attribute name="class" optional="yes"/>
- <attribute name="name"/>
- </environment>
-
- <environment name="methoddesc">
- <attribute name="class" optional="yes"/>
- <attribute name="name"/>
- <child name="args"/>
- </environment>
- <macro name="methodline">
- <attribute name="class" optional="yes"/>
- <attribute name="name"/>
- <child name="args"/>
- </macro>
- <environment name="methoddescni">
- <attribute name="index">no</attribute>
- <attribute name="class" optional="yes"/>
- <attribute name="name"/>
- <child name="args"/>
- </environment>
- <macro name="methodlineni" outputname="methodline">
- <attribute name="index">no</attribute>
- <attribute name="class" optional="yes"/>
- <attribute name="name"/>
- <child name="args"/>
- </macro>
-
- <environment name="opcodedesc">
- <attribute name="name"/>
- <attribute name="var"/>
- </environment>
-
- <!-- "See also:" sections. -->
- <macro name="seemodule">
- <attribute name="ref" optional="yes"/>
- <attribute name="name"/>
- <child name="description"/>
- </macro>
- <macro name="seepep">
- <attribute name="number"/>
- <child name="title"/>
- <child name="description"/>
- </macro>
- <macro name="seerfc">
- <attribute name="number"/>
- <child name="title"/>
- <child name="description"/>
- </macro>
- <macro name="seetext">
- <child name="description"/>
- </macro>
- <macro name="seetitle">
- <attribute name="href" optional="yes"/>
- <child name="title"/>
- <child name="description"/>
- </macro>
- <macro name="seeurl">
- <attribute name="href"/>
- <child name="description"/>
- </macro>
-
- <!-- Index-generating markup. -->
- <macro name="index" outputname="indexterm">
- <attribute name="term1"/>
- </macro>
- <macro name="indexii" outputname="indexterm">
- <attribute name="term1"/>
- <attribute name="term2"/>
- </macro>
- <macro name="indexiii" outputname="indexterm">
- <attribute name="term1"/>
- <attribute name="term2"/>
- <attribute name="term3"/>
- </macro>
- <macro name="indexiv" outputname="indexterm">
- <attribute name="term1"/>
- <attribute name="term2"/>
- <attribute name="term3"/>
- <attribute name="term4"/>
- </macro>
-
- <macro name="ttindex" outputname="indexterm">
- <attribute name="style">tt</attribute>
- <attribute name="term1"/>
- </macro>
-
- <macro name="refmodindex">
- <attribute name="module"/>
- </macro>
- <macro name="stmodindex">
- <attribute name="module"/>
- </macro>
- <macro name="refbimodindex" outputname="refmodindex">
- <attribute name="module"/>
- </macro>
- <macro name="refexmodindex" outputname="refmodindex">
- <attribute name="module"/>
- </macro>
- <macro name="refstmodindex" outputname="refmodindex">
- <attribute name="module"/>
- </macro>
-
- <macro name="bifuncindex">
- <attribute name="name"/>
- </macro>
- <macro name="exindex">
- <attribute name="name"/>
- </macro>
- <macro name="obindex">
- <attribute name="name"/>
- </macro>
- <macro name="kwindex">
- <attribute name="name"/>
- </macro>
- <macro name="opindex">
- <attribute name="type"/>
- </macro>
- <macro name="stindex">
- <attribute name="type"/>
- </macro>
- <macro name="withsubitem">
- <attribute name="text"/>
- <content/>
- </macro>
- <macro name="setindexsubitem">
- <attribute name="text"/>
- </macro>
-
- <!-- Entity management. -->
- <macro name="include">
- <attribute name="source"/>
- </macro>
- <macro name="input">
- <attribute name="source"/>
- </macro>
-
- <!-- Large-scale document structure. -->
- <macro name="documentclass">
- <attribute name="classname"/>
- </macro>
-
- <macro name="usepackage">
- <attribute name="options" optional="yes"/>
- <attribute name="pkg"/>
- </macro>
-
- <environment name="document"
- endcloses="chapter chapter* section section*
- subsection subsection*
- subsubsection subsubsection*
- paragraph paragraph* subparagraph subparagraph*"/>
-
- <macro name="chapter"
- closes="chapter chapter* section section* subsection subsection*
- subsubsection subsubsection*
- paragraph paragraph* subparagraph subparagraph*">
- <text>
-</text>
- <child name="title"/>
- <content implied="yes"/>
- </macro>
- <macro name="chapter*" outputname="chapter"
- closes="chapter chapter* section section* subsection subsection*
- subsubsection subsubsection*
- paragraph paragraph* subparagraph subparagraph*">
- <attribute name="numbered">no</attribute>
- <text>
-</text>
- <child name="title"/>
- <content implied="yes"/>
- </macro>
-
- <macro name="section"
- closes="section section* subsection subsection*
- subsubsection subsubsection*
- paragraph paragraph* subparagraph subparagraph*">
- <text>
-</text>
- <child name="title"/>
- <content implied="yes"/>
- </macro>
- <macro name="section*" outputname="section"
- closes="section section* subsection subsection*
- subsubsection subsubsection*
- paragraph paragraph* subparagraph subparagraph*">
- <attribute name="numbered">no</attribute>
- <text>
-</text>
- <child name="title"/>
- <content implied="yes"/>
- </macro>
-
- <macro name="subsection"
- closes="subsection subsection* subsubsection subsubsection*
- paragraph paragraph* subparagraph subparagraph*">
- <text>
-</text>
- <child name="title"/>
- <content implied="yes"/>
- </macro>
- <macro name="subsection*" outputname="subsection"
- closes="subsection subsection* subsubsection subsubsection*
- paragraph paragraph* subparagraph subparagraph*">
- <attribute name="numbered">no</attribute>
- <text>
-</text>
- <child name="title"/>
- <content implied="yes"/>
- </macro>
-
- <macro name="subsubsection"
- closes="subsubsection subsubsection*
- paragraph paragraph* subparagraph subparagraph*">
- <text>
-</text>
- <child name="title"/>
- <content implied="yes"/>
- </macro>
- <macro name="subsubsection*" outputname="subsubsection"
- closes="subsubsection subsubsection*
- paragraph paragraph* subparagraph subparagraph*">
- <attribute name="numbered">no</attribute>
- <text>
-</text>
- <child name="title"/>
- <content implied="yes"/>
- </macro>
-
- <macro name="paragraph"
- closes="paragraph paragraph* subparagraph subparagraph*">
- <text>
-</text>
- <child name="title"/>
- <content implied="yes"/>
- </macro>
- <macro name="paragraph*" outputname="paragraph"
- closes="paragraph paragraph* subparagraph subparagraph*">
- <attribute name="numbered">no</attribute>
- <text>
-</text>
- <child name="title"/>
- <content implied="yes"/>
- </macro>
-
- <macro name="subparagraph"
- closes="subparagraph subparagraph*">
- <text>
-</text>
- <child name="title"/>
- <content implied="yes"/>
- </macro>
- <macro name="subparagraph*" outputname="subparagraph"
- closes="subparagraph subparagraph*">
- <attribute name="numbered">no</attribute>
- <text>
-</text>
- <child name="title"/>
- <content implied="yes"/>
- </macro>
- <macro name="title">
- <content/>
- </macro>
-
- <macro name="appendix" outputname="back-matter"
- closes="chapter chapter* section subsection subsubsection
- paragraph subparagraph"/>
-
- <environment name="list"
- endcloses="item">
- <attribute name="bullet"/>
- <attribute name="init"/>
- </environment>
- <macro name="item" closes="item">
- <child name="leader" optional="yes"/>
- <content implied="yes"/>
- </macro>
-
- <macro name="ref">
- <attribute name="ref"/>
- </macro>
-
- <environment name="description" outputname="descriptionlist"
- endcloses="item"/>
-
- <environment name="enumerate" outputname="enumeration"
- endcloses="item"/>
-
- <environment name="fulllineitems"
- endcloses="item"/>
-
- <environment name="itemize"
- endcloses="item"/>
-
- <environment name="definitions" outputname="definitionlist"
- encloses="term"/>
- <macro name="term" closes="definition">
- <!-- not really optional, but uses the [] syntax -->
- <child name="term" optional="yes"/>
- <child name="definition" implied="yes"/>
- </macro>
-
- <environment name="alltt" outputname="verbatim"/>
- <environment name="comment" verbatim="yes"/>
- <environment name="verbatim" verbatim="yes"/>
- <environment name="verbatim*" verbatim="yes">
- <!-- not used anywhere, but it's a standard LaTeXism -->
- <attribute name="spaces">visible</attribute>
- </environment>
-
- <!-- Table markup. -->
- <macro name="hline"/>
- <environment name="tableii" outputname="table">
- <attribute name="cols">2</attribute>
- <attribute name="colspec"/>
- <attribute name="style"/>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- </environment>
- <environment name="longtableii" outputname="table">
- <attribute name="cols">2</attribute>
- <attribute name="colspec"/>
- <attribute name="style"/>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- </environment>
- <macro name="lineii" outputname="row">
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- </macro>
-
- <environment name="tableiii" outputname="table">
- <attribute name="cols">3</attribute>
- <attribute name="colspec"/>
- <attribute name="style"/>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- </environment>
- <environment name="longtableiii" outputname="table">
- <attribute name="cols">3</attribute>
- <attribute name="colspec"/>
- <attribute name="style"/>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- </environment>
- <macro name="lineiii" outputname="row">
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- </macro>
-
- <environment name="tableiv" outputname="table">
- <attribute name="cols">4</attribute>
- <attribute name="colspec"/>
- <attribute name="style"/>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- </environment>
- <environment name="longtableiv" outputname="table">
- <attribute name="cols">4</attribute>
- <attribute name="colspec"/>
- <attribute name="style"/>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- </environment>
- <macro name="lineiv" outputname="row">
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- <text>
- </text>
- <child name="entry"/>
- </macro>
-
- <!-- These are handled at a later translation stage, at least for now. -->
- <macro name="Cpp" outputname="">
- <text>C++</text>
- </macro>
- <macro name="geq" outputname="">
- <entityref name="geq"/>
- </macro>
- <macro name="LaTeX" outputname="">
- <text>LaTeX</text>
- </macro>
- <macro name="ldots" outputname="">
- <text>...</text>
- </macro>
- <macro name="leq" outputname="">
- <entityref name="leq"/>
- </macro>
- <macro name="TeX" outputname="">
- <text>TeX</text>
- </macro>
- <macro name="version"/>
-
- <!-- Distutils things. -->
- <macro name="command">
- <content/>
- </macro>
- <macro name="option">
- <content/>
- </macro>
- <macro name="filevar" outputname="var">
- <content/>
- </macro>
- <macro name="XXX" outputname="editorial-comment">
- <content/>
- </macro>
-
- <!-- Misc. -->
- <macro name="emph">
- <content/>
- </macro>
- <macro name="strong">
- <content/>
- </macro>
- <macro name="textrm">
- <content/>
- </macro>
- <macro name="texttt">
- <content/>
- </macro>
- <macro name="code">
- <content/>
- </macro>
- <macro name="exception">
- <content/>
- </macro>
- <macro name="keyword">
- <content/>
- </macro>
- <macro name="samp">
- <content/>
- </macro>
- <macro name="class">
- <content/>
- </macro>
- <macro name="cdata">
- <content/>
- </macro>
- <macro name="cfunction">
- <content/>
- </macro>
- <macro name="ctype">
- <content/>
- </macro>
- <macro name="pytype">
- <content/>
- </macro>
- <macro name="character">
- <content/>
- </macro>
- <macro name="constant">
- <content/>
- </macro>
- <macro name="envvar" outputname="envar">
- <content/>
- </macro>
- <macro name="file" outputname="filename">
- <content/>
- </macro>
- <macro name="filenq" outputname="filename">
- <attribute name="quote">no</attribute>
- <content/>
- </macro>
- <macro name="function">
- <content/>
- </macro>
- <macro name="kbd">
- <content/>
- </macro>
- <macro name="makevar">
- <content/>
- </macro>
- <macro name="method">
- <content/>
- </macro>
- <macro name="member">
- <content/>
- </macro>
- <macro name="mimetype">
- <content/>
- </macro>
- <macro name="newsgroup">
- <content/>
- </macro>
- <macro name="program" outputname="command">
- <content/>
- </macro>
- <macro name="programopt" outputname="option">
- <content/>
- </macro>
- <macro name="longprogramopt" outputname="longoption">
- <content/>
- </macro>
- <macro name="regexp">
- <content/>
- </macro>
- <macro name="var">
- <content/>
- </macro>
- <macro name="email">
- <content/>
- </macro>
- <macro name="url">
- <content/>
- </macro>
- <macro name="footnote">
- <content/>
- </macro>
- <macro name="dfn" outputname="definedterm">
- <content/>
- </macro>
-
- <macro name="mbox">
- <content/>
- </macro>
-
- <!-- minimal math stuff to get by -->
- <macro name="pi"/>
- <macro name="sqrt">
- <content/>
- </macro>
- <macro name="frac" outputname="fraction">
- <child name="numerator"/>
- <child name="denominator"/>
- </macro>
- <macro name="sum">
- <content/>
- </macro>
-
- <!-- Conversions to text; perhaps could be different? There's -->
- <!-- no way for a style sheet to work with these this way. -->
- <macro name="ABC" outputname="">
- <text>ABC</text>
- </macro>
- <macro name="ASCII" outputname="">
- <text>ASCII</text>
- </macro>
- <macro name="C" outputname="">
- <text>C</text>
- </macro>
- <macro name="EOF" outputname="">
- <text>EOF</text>
- </macro>
- <macro name="e" outputname="">
- <text>\</text>
- </macro>
- <macro name="NULL" outputname="constant">
- <text>NULL</text>
- </macro>
- <macro name="POSIX" outputname="">
- <text>POSIX</text>
- </macro>
- <macro name="UNIX" outputname="">
- <text>Unix</text>
- </macro>
- <macro name="textasciitilde" outputname="">
- <text>~</text>
- </macro>
-
- <!-- These will end up disappearing as well! -->
- <macro name="catcode" outputname=""/>
- <macro name="fi" outputname=""/>
- <macro name="ifhtml" outputname=""/>
- <macro name="indexname" outputname=""/>
- <macro name="labelwidth" outputname=""/>
- <macro name="large" outputname=""/>
- <macro name="leftmargin" outputname=""/>
- <macro name="makeindex" outputname=""/>
- <macro name="makemodindex" outputname=""/>
- <macro name="maketitle" outputname=""/>
- <macro name="noindent" outputname=""/>
- <macro name="protect" outputname=""/>
- <macro name="renewcommand">
- <attribute name="macro"/>
- <attribute name="nargs" optional="yes"/>
- <content/>
- </macro>
- <macro name="tableofcontents" outputname=""/>
- <macro name="vspace">
- <attribute name="size"/>
- </macro>
-</conversion>
+++ /dev/null
-#! /usr/bin/env python
-
-"""Perform massive transformations on a document tree created from the LaTeX
-of the Python documentation, and dump the ESIS data for the transformed tree.
-"""
-
-
-import errno
-import esistools
-import re
-import string
-import sys
-import xml.dom
-import xml.dom.minidom
-
-ELEMENT = xml.dom.Node.ELEMENT_NODE
-ENTITY_REFERENCE = xml.dom.Node.ENTITY_REFERENCE_NODE
-TEXT = xml.dom.Node.TEXT_NODE
-
-
-class ConversionError(Exception):
- pass
-
-
-ewrite = sys.stderr.write
-try:
- # We can only do this trick on Unix (if tput is on $PATH)!
- if sys.platform != "posix" or not sys.stderr.isatty():
- raise ImportError
- import commands
-except ImportError:
- bwrite = ewrite
-else:
- def bwrite(s, BOLDON=commands.getoutput("tput bold"),
- BOLDOFF=commands.getoutput("tput sgr0")):
- ewrite("%s%s%s" % (BOLDON, s, BOLDOFF))
-
-
-PARA_ELEMENT = "para"
-
-DEBUG_PARA_FIXER = 0
-
-if DEBUG_PARA_FIXER:
- def para_msg(s):
- ewrite("*** %s\n" % s)
-else:
- def para_msg(s):
- pass
-
-
-def get_first_element(doc, gi):
- for n in doc.childNodes:
- if n.nodeName == gi:
- return n
-
-def extract_first_element(doc, gi):
- node = get_first_element(doc, gi)
- if node is not None:
- doc.removeChild(node)
- return node
-
-
-def get_documentElement(node):
- result = None
- for child in node.childNodes:
- if child.nodeType == ELEMENT:
- result = child
- return result
-
-
-def set_tagName(elem, gi):
- elem.nodeName = elem.tagName = gi
-
-
-def find_all_elements(doc, gi):
- nodes = []
- if doc.nodeName == gi:
- nodes.append(doc)
- for child in doc.childNodes:
- if child.nodeType == ELEMENT:
- if child.tagName == gi:
- nodes.append(child)
- for node in child.getElementsByTagName(gi):
- nodes.append(node)
- return nodes
-
-def find_all_child_elements(doc, gi):
- nodes = []
- for child in doc.childNodes:
- if child.nodeName == gi:
- nodes.append(child)
- return nodes
-
-
-def find_all_elements_from_set(doc, gi_set):
- return __find_all_elements_from_set(doc, gi_set, [])
-
-def __find_all_elements_from_set(doc, gi_set, nodes):
- if doc.nodeName in gi_set:
- nodes.append(doc)
- for child in doc.childNodes:
- if child.nodeType == ELEMENT:
- __find_all_elements_from_set(child, gi_set, nodes)
- return nodes
-
-
-def simplify(doc, fragment):
- # Try to rationalize the document a bit, since these things are simply
- # not valid SGML/XML documents as they stand, and need a little work.
- documentclass = "document"
- inputs = []
- node = extract_first_element(fragment, "documentclass")
- if node is not None:
- documentclass = node.getAttribute("classname")
- node = extract_first_element(fragment, "title")
- if node is not None:
- inputs.append(node)
- # update the name of the root element
- node = get_first_element(fragment, "document")
- if node is not None:
- set_tagName(node, documentclass)
- while 1:
- node = extract_first_element(fragment, "input")
- if node is None:
- break
- inputs.append(node)
- if inputs:
- docelem = get_documentElement(fragment)
- inputs.reverse()
- for node in inputs:
- text = doc.createTextNode("\n")
- docelem.insertBefore(text, docelem.firstChild)
- docelem.insertBefore(node, text)
- docelem.insertBefore(doc.createTextNode("\n"), docelem.firstChild)
- while fragment.firstChild and fragment.firstChild.nodeType == TEXT:
- fragment.removeChild(fragment.firstChild)
-
-
-def cleanup_root_text(doc):
- discards = []
- skip = 0
- for n in doc.childNodes:
- prevskip = skip
- skip = 0
- if n.nodeType == TEXT and not prevskip:
- discards.append(n)
- elif n.nodeName == "COMMENT":
- skip = 1
- for node in discards:
- doc.removeChild(node)
-
-
-DESCRIPTOR_ELEMENTS = (
- "cfuncdesc", "cvardesc", "ctypedesc",
- "classdesc", "memberdesc", "memberdescni", "methoddesc", "methoddescni",
- "excdesc", "funcdesc", "funcdescni", "opcodedesc",
- "datadesc", "datadescni",
- )
-
-def fixup_descriptors(doc, fragment):
- sections = find_all_elements(fragment, "section")
- for section in sections:
- find_and_fix_descriptors(doc, section)
-
-
-def find_and_fix_descriptors(doc, container):
- children = container.childNodes
- for child in children:
- if child.nodeType == ELEMENT:
- tagName = child.tagName
- if tagName in DESCRIPTOR_ELEMENTS:
- rewrite_descriptor(doc, child)
- elif tagName == "subsection":
- find_and_fix_descriptors(doc, child)
-
-
-def rewrite_descriptor(doc, descriptor):
- #
- # Do these things:
- # 1. Add an "index='no'" attribute to the element if the tagName
- # ends in 'ni', removing the 'ni' from the name.
- # 2. Create a <signature> from the name attribute
- # 2a.Create an <args> if it appears to be available.
- # 3. Create additional <signature>s from <*line{,ni}> elements,
- # if found.
- # 4. If a <versionadded> is found, move it to an attribute on the
- # descriptor.
- # 5. Move remaining child nodes to a <description> element.
- # 6. Put it back together.
- #
- # 1.
- descname = descriptor.tagName
- index = 1
- if descname[-2:] == "ni":
- descname = descname[:-2]
- descriptor.setAttribute("index", "no")
- set_tagName(descriptor, descname)
- index = 0
- desctype = descname[:-4] # remove 'desc'
- linename = desctype + "line"
- if not index:
- linename = linename + "ni"
- # 2.
- signature = doc.createElement("signature")
- name = doc.createElement("name")
- signature.appendChild(doc.createTextNode("\n "))
- signature.appendChild(name)
- name.appendChild(doc.createTextNode(descriptor.getAttribute("name")))
- descriptor.removeAttribute("name")
- # 2a.
- if descriptor.hasAttribute("var"):
- if descname != "opcodedesc":
- raise RuntimeError, \
- "got 'var' attribute on descriptor other than opcodedesc"
- variable = descriptor.getAttribute("var")
- if variable:
- args = doc.createElement("args")
- args.appendChild(doc.createTextNode(variable))
- signature.appendChild(doc.createTextNode("\n "))
- signature.appendChild(args)
- descriptor.removeAttribute("var")
- newchildren = [signature]
- children = descriptor.childNodes
- pos = skip_leading_nodes(children)
- if pos < len(children):
- child = children[pos]
- if child.nodeName == "args":
- # move <args> to <signature>, or remove if empty:
- child.parentNode.removeChild(child)
- if len(child.childNodes):
- signature.appendChild(doc.createTextNode("\n "))
- signature.appendChild(child)
- signature.appendChild(doc.createTextNode("\n "))
- # 3, 4.
- pos = skip_leading_nodes(children, pos)
- while pos < len(children) \
- and children[pos].nodeName in (linename, "versionadded"):
- if children[pos].tagName == linename:
- # this is really a supplemental signature, create <signature>
- oldchild = children[pos].cloneNode(1)
- try:
- sig = methodline_to_signature(doc, children[pos])
- except KeyError:
- print oldchild.toxml()
- raise
- newchildren.append(sig)
- else:
- # <versionadded added=...>
- descriptor.setAttribute(
- "added", children[pos].getAttribute("version"))
- pos = skip_leading_nodes(children, pos + 1)
- # 5.
- description = doc.createElement("description")
- description.appendChild(doc.createTextNode("\n"))
- newchildren.append(description)
- move_children(descriptor, description, pos)
- last = description.childNodes[-1]
- if last.nodeType == TEXT:
- last.data = string.rstrip(last.data) + "\n "
- # 6.
- # should have nothing but whitespace and signature lines in <descriptor>;
- # discard them
- while descriptor.childNodes:
- descriptor.removeChild(descriptor.childNodes[0])
- for node in newchildren:
- descriptor.appendChild(doc.createTextNode("\n "))
- descriptor.appendChild(node)
- descriptor.appendChild(doc.createTextNode("\n"))
-
-
-def methodline_to_signature(doc, methodline):
- signature = doc.createElement("signature")
- signature.appendChild(doc.createTextNode("\n "))
- name = doc.createElement("name")
- name.appendChild(doc.createTextNode(methodline.getAttribute("name")))
- methodline.removeAttribute("name")
- signature.appendChild(name)
- if len(methodline.childNodes):
- args = doc.createElement("args")
- signature.appendChild(doc.createTextNode("\n "))
- signature.appendChild(args)
- move_children(methodline, args)
- signature.appendChild(doc.createTextNode("\n "))
- return signature
-
-
-def move_children(origin, dest, start=0):
- children = origin.childNodes
- while start < len(children):
- node = children[start]
- origin.removeChild(node)
- dest.appendChild(node)
-
-
-def handle_appendix(doc, fragment):
- # must be called after simplfy() if document is multi-rooted to begin with
- docelem = get_documentElement(fragment)
- toplevel = docelem.tagName == "manual" and "chapter" or "section"
- appendices = 0
- nodes = []
- for node in docelem.childNodes:
- if appendices:
- nodes.append(node)
- elif node.nodeType == ELEMENT:
- appnodes = node.getElementsByTagName("appendix")
- if appnodes:
- appendices = 1
- parent = appnodes[0].parentNode
- parent.removeChild(appnodes[0])
- parent.normalize()
- if nodes:
- map(docelem.removeChild, nodes)
- docelem.appendChild(doc.createTextNode("\n\n\n"))
- back = doc.createElement("back-matter")
- docelem.appendChild(back)
- back.appendChild(doc.createTextNode("\n"))
- while nodes and nodes[0].nodeType == TEXT \
- and not string.strip(nodes[0].data):
- del nodes[0]
- map(back.appendChild, nodes)
- docelem.appendChild(doc.createTextNode("\n"))
-
-
-def handle_labels(doc, fragment):
- for label in find_all_elements(fragment, "label"):
- id = label.getAttribute("id")
- if not id:
- continue
- parent = label.parentNode
- parentTagName = parent.tagName
- if parentTagName == "title":
- parent.parentNode.setAttribute("id", id)
- else:
- parent.setAttribute("id", id)
- # now, remove <label id="..."/> from parent:
- parent.removeChild(label)
- if parentTagName == "title":
- parent.normalize()
- children = parent.childNodes
- if children[-1].nodeType == TEXT:
- children[-1].data = string.rstrip(children[-1].data)
-
-
-def fixup_trailing_whitespace(doc, wsmap):
- queue = [doc]
- while queue:
- node = queue[0]
- del queue[0]
- if wsmap.has_key(node.nodeName):
- ws = wsmap[node.tagName]
- children = node.childNodes
- children.reverse()
- if children[0].nodeType == TEXT:
- data = string.rstrip(children[0].data) + ws
- children[0].data = data
- children.reverse()
- # hack to get the title in place:
- if node.tagName == "title" \
- and node.parentNode.firstChild.nodeType == ELEMENT:
- node.parentNode.insertBefore(doc.createText("\n "),
- node.parentNode.firstChild)
- for child in node.childNodes:
- if child.nodeType == ELEMENT:
- queue.append(child)
-
-
-def normalize(doc):
- for node in doc.childNodes:
- if node.nodeType == ELEMENT:
- node.normalize()
-
-
-def cleanup_trailing_parens(doc, element_names):
- d = {}
- for gi in element_names:
- d[gi] = gi
- rewrite_element = d.has_key
- queue = []
- for node in doc.childNodes:
- if node.nodeType == ELEMENT:
- queue.append(node)
- while queue:
- node = queue[0]
- del queue[0]
- if rewrite_element(node.tagName):
- children = node.childNodes
- if len(children) == 1 \
- and children[0].nodeType == TEXT:
- data = children[0].data
- if data[-2:] == "()":
- children[0].data = data[:-2]
- else:
- for child in node.childNodes:
- if child.nodeType == ELEMENT:
- queue.append(child)
-
-
-def contents_match(left, right):
- left_children = left.childNodes
- right_children = right.childNodes
- if len(left_children) != len(right_children):
- return 0
- for l, r in map(None, left_children, right_children):
- nodeType = l.nodeType
- if nodeType != r.nodeType:
- return 0
- if nodeType == ELEMENT:
- if l.tagName != r.tagName:
- return 0
- # should check attributes, but that's not a problem here
- if not contents_match(l, r):
- return 0
- elif nodeType == TEXT:
- if l.data != r.data:
- return 0
- else:
- # not quite right, but good enough
- return 0
- return 1
-
-
-def create_module_info(doc, section):
- # Heavy.
- node = extract_first_element(section, "modulesynopsis")
- if node is None:
- return
- set_tagName(node, "synopsis")
- lastchild = node.childNodes[-1]
- if lastchild.nodeType == TEXT \
- and lastchild.data[-1:] == ".":
- lastchild.data = lastchild.data[:-1]
- modauthor = extract_first_element(section, "moduleauthor")
- if modauthor:
- set_tagName(modauthor, "author")
- modauthor.appendChild(doc.createTextNode(
- modauthor.getAttribute("name")))
- modauthor.removeAttribute("name")
- platform = extract_first_element(section, "platform")
- if section.tagName == "section":
- modinfo_pos = 2
- modinfo = doc.createElement("moduleinfo")
- moddecl = extract_first_element(section, "declaremodule")
- name = None
- if moddecl:
- modinfo.appendChild(doc.createTextNode("\n "))
- name = moddecl.attributes["name"].value
- namenode = doc.createElement("name")
- namenode.appendChild(doc.createTextNode(name))
- modinfo.appendChild(namenode)
- type = moddecl.attributes.get("type")
- if type:
- type = type.value
- modinfo.appendChild(doc.createTextNode("\n "))
- typenode = doc.createElement("type")
- typenode.appendChild(doc.createTextNode(type))
- modinfo.appendChild(typenode)
- versionadded = extract_first_element(section, "versionadded")
- if versionadded:
- modinfo.setAttribute("added", versionadded.getAttribute("version"))
- title = get_first_element(section, "title")
- if title:
- children = title.childNodes
- if len(children) >= 2 \
- and children[0].nodeName == "module" \
- and children[0].childNodes[0].data == name:
- # this is it; morph the <title> into <short-synopsis>
- first_data = children[1]
- if first_data.data[:4] == " ---":
- first_data.data = string.lstrip(first_data.data[4:])
- set_tagName(title, "short-synopsis")
- if children[-1].nodeType == TEXT \
- and children[-1].data[-1:] == ".":
- children[-1].data = children[-1].data[:-1]
- section.removeChild(title)
- section.removeChild(section.childNodes[0])
- title.removeChild(children[0])
- modinfo_pos = 0
- else:
- ewrite("module name in title doesn't match"
- " <declaremodule/>; no <short-synopsis/>\n")
- else:
- ewrite("Unexpected condition: <section/> without <title/>\n")
- modinfo.appendChild(doc.createTextNode("\n "))
- modinfo.appendChild(node)
- if title and not contents_match(title, node):
- # The short synopsis is actually different,
- # and needs to be stored:
- modinfo.appendChild(doc.createTextNode("\n "))
- modinfo.appendChild(title)
- if modauthor:
- modinfo.appendChild(doc.createTextNode("\n "))
- modinfo.appendChild(modauthor)
- if platform:
- modinfo.appendChild(doc.createTextNode("\n "))
- modinfo.appendChild(platform)
- modinfo.appendChild(doc.createTextNode("\n "))
- section.insertBefore(modinfo, section.childNodes[modinfo_pos])
- section.insertBefore(doc.createTextNode("\n "), modinfo)
- #
- # The rest of this removes extra newlines from where we cut out
- # a lot of elements. A lot of code for minimal value, but keeps
- # keeps the generated *ML from being too funny looking.
- #
- section.normalize()
- children = section.childNodes
- for i in range(len(children)):
- node = children[i]
- if node.nodeName == "moduleinfo":
- nextnode = children[i+1]
- if nextnode.nodeType == TEXT:
- data = nextnode.data
- if len(string.lstrip(data)) < (len(data) - 4):
- nextnode.data = "\n\n\n" + string.lstrip(data)
-
-
-def cleanup_synopses(doc, fragment):
- for node in find_all_elements(fragment, "section"):
- create_module_info(doc, node)
-
-
-def fixup_table_structures(doc, fragment):
- for table in find_all_elements(fragment, "table"):
- fixup_table(doc, table)
-
-
-def fixup_table(doc, table):
- # create the table head
- thead = doc.createElement("thead")
- row = doc.createElement("row")
- move_elements_by_name(doc, table, row, "entry")
- thead.appendChild(doc.createTextNode("\n "))
- thead.appendChild(row)
- thead.appendChild(doc.createTextNode("\n "))
- # create the table body
- tbody = doc.createElement("tbody")
- prev_row = None
- last_was_hline = 0
- children = table.childNodes
- for child in children:
- if child.nodeType == ELEMENT:
- tagName = child.tagName
- if tagName == "hline" and prev_row is not None:
- prev_row.setAttribute("rowsep", "1")
- elif tagName == "row":
- prev_row = child
- # save the rows:
- tbody.appendChild(doc.createTextNode("\n "))
- move_elements_by_name(doc, table, tbody, "row", sep="\n ")
- # and toss the rest:
- while children:
- child = children[0]
- nodeType = child.nodeType
- if nodeType == TEXT:
- if string.strip(child.data):
- raise ConversionError("unexpected free data in <%s>: %r"
- % (table.tagName, child.data))
- table.removeChild(child)
- continue
- if nodeType == ELEMENT:
- if child.tagName != "hline":
- raise ConversionError(
- "unexpected <%s> in table" % child.tagName)
- table.removeChild(child)
- continue
- raise ConversionError(
- "unexpected %s node in table" % child.__class__.__name__)
- # nothing left in the <table>; add the <thead> and <tbody>
- tgroup = doc.createElement("tgroup")
- tgroup.appendChild(doc.createTextNode("\n "))
- tgroup.appendChild(thead)
- tgroup.appendChild(doc.createTextNode("\n "))
- tgroup.appendChild(tbody)
- tgroup.appendChild(doc.createTextNode("\n "))
- table.appendChild(tgroup)
- # now make the <entry>s look nice:
- for row in table.getElementsByTagName("row"):
- fixup_row(doc, row)
-
-
-def fixup_row(doc, row):
- entries = []
- map(entries.append, row.childNodes[1:])
- for entry in entries:
- row.insertBefore(doc.createTextNode("\n "), entry)
-# row.appendChild(doc.createTextNode("\n "))
-
-
-def move_elements_by_name(doc, source, dest, name, sep=None):
- nodes = []
- for child in source.childNodes:
- if child.nodeName == name:
- nodes.append(child)
- for node in nodes:
- source.removeChild(node)
- dest.appendChild(node)
- if sep:
- dest.appendChild(doc.createTextNode(sep))
-
-
-RECURSE_INTO_PARA_CONTAINERS = (
- "chapter", "abstract", "enumerate",
- "section", "subsection", "subsubsection",
- "paragraph", "subparagraph", "back-matter",
- "howto", "manual",
- "item", "itemize", "fulllineitems", "enumeration", "descriptionlist",
- "definitionlist", "definition",
- )
-
-PARA_LEVEL_ELEMENTS = (
- "moduleinfo", "title", "verbatim", "enumerate", "item",
- "interpreter-session", "back-matter", "interactive-session",
- "opcodedesc", "classdesc", "datadesc",
- "funcdesc", "methoddesc", "excdesc", "memberdesc", "membderdescni",
- "funcdescni", "methoddescni", "excdescni",
- "tableii", "tableiii", "tableiv", "localmoduletable",
- "sectionauthor", "seealso", "itemize",
- # include <para>, so we can just do it again to get subsequent paras:
- PARA_ELEMENT,
- )
-
-PARA_LEVEL_PRECEEDERS = (
- "setindexsubitem", "author",
- "stindex", "obindex", "COMMENT", "label", "input", "title",
- "versionadded", "versionchanged", "declaremodule", "modulesynopsis",
- "moduleauthor", "indexterm", "leader",
- )
-
-
-def fixup_paras(doc, fragment):
- for child in fragment.childNodes:
- if child.nodeName in RECURSE_INTO_PARA_CONTAINERS:
- fixup_paras_helper(doc, child)
- descriptions = find_all_elements(fragment, "description")
- for description in descriptions:
- fixup_paras_helper(doc, description)
-
-
-def fixup_paras_helper(doc, container, depth=0):
- # document is already normalized
- children = container.childNodes
- start = skip_leading_nodes(children)
- while len(children) > start:
- if children[start].nodeName in RECURSE_INTO_PARA_CONTAINERS:
- # Something to recurse into:
- fixup_paras_helper(doc, children[start])
- else:
- # Paragraph material:
- build_para(doc, container, start, len(children))
- if DEBUG_PARA_FIXER and depth == 10:
- sys.exit(1)
- start = skip_leading_nodes(children, start + 1)
-
-
-def build_para(doc, parent, start, i):
- children = parent.childNodes
- after = start + 1
- have_last = 0
- BREAK_ELEMENTS = PARA_LEVEL_ELEMENTS + RECURSE_INTO_PARA_CONTAINERS
- # Collect all children until \n\n+ is found in a text node or a
- # member of BREAK_ELEMENTS is found.
- for j in range(start, i):
- after = j + 1
- child = children[j]
- nodeType = child.nodeType
- if nodeType == ELEMENT:
- if child.tagName in BREAK_ELEMENTS:
- after = j
- break
- elif nodeType == TEXT:
- pos = string.find(child.data, "\n\n")
- if pos == 0:
- after = j
- break
- if pos >= 1:
- child.splitText(pos)
- break
- else:
- have_last = 1
- if (start + 1) > after:
- raise ConversionError(
- "build_para() could not identify content to turn into a paragraph")
- if children[after - 1].nodeType == TEXT:
- # we may need to split off trailing white space:
- child = children[after - 1]
- data = child.data
- if string.rstrip(data) != data:
- have_last = 0
- child.splitText(len(string.rstrip(data)))
- para = doc.createElement(PARA_ELEMENT)
- prev = None
- indexes = range(start, after)
- indexes.reverse()
- for j in indexes:
- node = parent.childNodes[j]
- parent.removeChild(node)
- para.insertBefore(node, prev)
- prev = node
- if have_last:
- parent.appendChild(para)
- parent.appendChild(doc.createTextNode("\n\n"))
- return len(parent.childNodes)
- else:
- nextnode = parent.childNodes[start]
- if nextnode.nodeType == TEXT:
- if nextnode.data and nextnode.data[0] != "\n":
- nextnode.data = "\n" + nextnode.data
- else:
- newnode = doc.createTextNode("\n")
- parent.insertBefore(newnode, nextnode)
- nextnode = newnode
- start = start + 1
- parent.insertBefore(para, nextnode)
- return start + 1
-
-
-def skip_leading_nodes(children, start=0):
- """Return index into children of a node at which paragraph building should
- begin or a recursive call to fixup_paras_helper() should be made (for
- subsections, etc.).
-
- When the return value >= len(children), we've built all the paras we can
- from this list of children.
- """
- i = len(children)
- while i > start:
- # skip over leading comments and whitespace:
- child = children[start]
- nodeType = child.nodeType
- if nodeType == TEXT:
- data = child.data
- shortened = string.lstrip(data)
- if shortened:
- if data != shortened:
- # break into two nodes: whitespace and non-whitespace
- child.splitText(len(data) - len(shortened))
- return start + 1
- return start
- # all whitespace, just skip
- elif nodeType == ELEMENT:
- tagName = child.tagName
- if tagName in RECURSE_INTO_PARA_CONTAINERS:
- return start
- if tagName not in PARA_LEVEL_ELEMENTS + PARA_LEVEL_PRECEEDERS:
- return start
- start = start + 1
- return start
-
-
-def fixup_rfc_references(doc, fragment):
- for rfcnode in find_all_elements(fragment, "rfc"):
- rfcnode.appendChild(doc.createTextNode(
- "RFC " + rfcnode.getAttribute("num")))
-
-
-def fixup_signatures(doc, fragment):
- for child in fragment.childNodes:
- if child.nodeType == ELEMENT:
- args = child.getElementsByTagName("args")
- for arg in args:
- fixup_args(doc, arg)
- arg.normalize()
- args = child.getElementsByTagName("constructor-args")
- for arg in args:
- fixup_args(doc, arg)
- arg.normalize()
-
-
-def fixup_args(doc, arglist):
- for child in arglist.childNodes:
- if child.nodeName == "optional":
- # found it; fix and return
- arglist.insertBefore(doc.createTextNode("["), child)
- optkids = child.childNodes
- while optkids:
- k = optkids[0]
- child.removeChild(k)
- arglist.insertBefore(k, child)
- arglist.insertBefore(doc.createTextNode("]"), child)
- arglist.removeChild(child)
- return fixup_args(doc, arglist)
-
-
-def fixup_sectionauthors(doc, fragment):
- for sectauth in find_all_elements(fragment, "sectionauthor"):
- section = sectauth.parentNode
- section.removeChild(sectauth)
- set_tagName(sectauth, "author")
- sectauth.appendChild(doc.createTextNode(
- sectauth.getAttribute("name")))
- sectauth.removeAttribute("name")
- after = section.childNodes[2]
- title = section.childNodes[1]
- if title.nodeName != "title":
- after = section.childNodes[0]
- section.insertBefore(doc.createTextNode("\n "), after)
- section.insertBefore(sectauth, after)
-
-
-def fixup_verbatims(doc):
- for verbatim in find_all_elements(doc, "verbatim"):
- child = verbatim.childNodes[0]
- if child.nodeType == TEXT \
- and string.lstrip(child.data)[:3] == ">>>":
- set_tagName(verbatim, "interactive-session")
-
-
-def add_node_ids(fragment, counter=0):
- fragment.node_id = counter
- for node in fragment.childNodes:
- counter = counter + 1
- if node.nodeType == ELEMENT:
- counter = add_node_ids(node, counter)
- else:
- node.node_id = counter
- return counter + 1
-
-
-REFMODINDEX_ELEMENTS = ('refmodindex', 'refbimodindex',
- 'refexmodindex', 'refstmodindex')
-
-def fixup_refmodindexes(fragment):
- # Locate <ref*modindex>...</> co-located with <module>...</>, and
- # remove the <ref*modindex>, replacing it with index=index on the
- # <module> element.
- nodes = find_all_elements_from_set(fragment, REFMODINDEX_ELEMENTS)
- d = {}
- for node in nodes:
- parent = node.parentNode
- d[parent.node_id] = parent
- del nodes
- map(fixup_refmodindexes_chunk, d.values())
-
-
-def fixup_refmodindexes_chunk(container):
- # node is probably a <para>; let's see how often it isn't:
- if container.tagName != PARA_ELEMENT:
- bwrite("--- fixup_refmodindexes_chunk(%s)\n" % container)
- module_entries = find_all_elements(container, "module")
- if not module_entries:
- return
- index_entries = find_all_elements_from_set(container, REFMODINDEX_ELEMENTS)
- removes = []
- for entry in index_entries:
- children = entry.childNodes
- if len(children) != 0:
- bwrite("--- unexpected number of children for %s node:\n"
- % entry.tagName)
- ewrite(entry.toxml() + "\n")
- continue
- found = 0
- module_name = entry.getAttribute("module")
- for node in module_entries:
- if len(node.childNodes) != 1:
- continue
- this_name = node.childNodes[0].data
- if this_name == module_name:
- found = 1
- node.setAttribute("index", "yes")
- if found:
- removes.append(entry)
- for node in removes:
- container.removeChild(node)
-
-
-def fixup_bifuncindexes(fragment):
- nodes = find_all_elements(fragment, 'bifuncindex')
- d = {}
- # make sure that each parent is only processed once:
- for node in nodes:
- parent = node.parentNode
- d[parent.node_id] = parent
- del nodes
- map(fixup_bifuncindexes_chunk, d.values())
-
-
-def fixup_bifuncindexes_chunk(container):
- removes = []
- entries = find_all_child_elements(container, "bifuncindex")
- function_entries = find_all_child_elements(container, "function")
- for entry in entries:
- function_name = entry.getAttribute("name")
- found = 0
- for func_entry in function_entries:
- t2 = func_entry.childNodes[0].data
- if t2[-2:] != "()":
- continue
- t2 = t2[:-2]
- if t2 == function_name:
- func_entry.setAttribute("index", "yes")
- func_entry.setAttribute("module", "__builtin__")
- if not found:
- found = 1
- removes.append(entry)
- for entry in removes:
- container.removeChild(entry)
-
-
-def join_adjacent_elements(container, gi):
- queue = [container]
- while queue:
- parent = queue.pop()
- i = 0
- children = parent.childNodes
- nchildren = len(children)
- while i < (nchildren - 1):
- child = children[i]
- if child.nodeName == gi:
- if children[i+1].nodeName == gi:
- ewrite("--- merging two <%s/> elements\n" % gi)
- child = children[i]
- nextchild = children[i+1]
- nextchildren = nextchild.childNodes
- while len(nextchildren):
- node = nextchildren[0]
- nextchild.removeChild(node)
- child.appendChild(node)
- parent.removeChild(nextchild)
- continue
- if child.nodeType == ELEMENT:
- queue.append(child)
- i = i + 1
-
-
-_token_rx = re.compile(r"[a-zA-Z][a-zA-Z0-9.-]*$")
-
-def write_esis(doc, ofp, knownempty):
- for node in doc.childNodes:
- nodeType = node.nodeType
- if nodeType == ELEMENT:
- gi = node.tagName
- if knownempty(gi):
- if node.hasChildNodes():
- raise ValueError, \
- "declared-empty node <%s> has children" % gi
- ofp.write("e\n")
- for k, value in node.attributes.items():
- if _token_rx.match(value):
- dtype = "TOKEN"
- else:
- dtype = "CDATA"
- ofp.write("A%s %s %s\n" % (k, dtype, esistools.encode(value)))
- ofp.write("(%s\n" % gi)
- write_esis(node, ofp, knownempty)
- ofp.write(")%s\n" % gi)
- elif nodeType == TEXT:
- ofp.write("-%s\n" % esistools.encode(node.data))
- elif nodeType == ENTITY_REFERENCE:
- ofp.write("&%s\n" % node.nodeName)
- else:
- raise RuntimeError, "unsupported node type: %s" % nodeType
-
-
-def convert(ifp, ofp):
- events = esistools.parse(ifp)
- toktype, doc = events.getEvent()
- fragment = doc.createDocumentFragment()
- events.expandNode(fragment)
-
- normalize(fragment)
- simplify(doc, fragment)
- handle_labels(doc, fragment)
- handle_appendix(doc, fragment)
- fixup_trailing_whitespace(doc, {
- "abstract": "\n",
- "title": "",
- "chapter": "\n\n",
- "section": "\n\n",
- "subsection": "\n\n",
- "subsubsection": "\n\n",
- "paragraph": "\n\n",
- "subparagraph": "\n\n",
- })
- cleanup_root_text(doc)
- cleanup_trailing_parens(fragment, ["function", "method", "cfunction"])
- cleanup_synopses(doc, fragment)
- fixup_descriptors(doc, fragment)
- fixup_verbatims(fragment)
- normalize(fragment)
- fixup_paras(doc, fragment)
- fixup_sectionauthors(doc, fragment)
- fixup_table_structures(doc, fragment)
- fixup_rfc_references(doc, fragment)
- fixup_signatures(doc, fragment)
- add_node_ids(fragment)
- fixup_refmodindexes(fragment)
- fixup_bifuncindexes(fragment)
- # Take care of ugly hacks in the LaTeX markup to avoid LaTeX and
- # LaTeX2HTML screwing with GNU-style long options (the '--' problem).
- join_adjacent_elements(fragment, "option")
- #
- d = {}
- for gi in events.parser.get_empties():
- d[gi] = gi
- if d.has_key("author"):
- del d["author"]
- if d.has_key("rfc"):
- del d["rfc"]
- knownempty = d.has_key
- #
- try:
- write_esis(fragment, ofp, knownempty)
- except IOError, (err, msg):
- # Ignore EPIPE; it just means that whoever we're writing to stopped
- # reading. The rest of the output would be ignored. All other errors
- # should still be reported,
- if err != errno.EPIPE:
- raise
-
-
-def main():
- if len(sys.argv) == 1:
- ifp = sys.stdin
- ofp = sys.stdout
- elif len(sys.argv) == 2:
- ifp = open(sys.argv[1])
- ofp = sys.stdout
- elif len(sys.argv) == 3:
- ifp = open(sys.argv[1])
- import StringIO
- ofp = StringIO.StringIO()
- else:
- usage()
- sys.exit(2)
- convert(ifp, ofp)
- if len(sys.argv) == 3:
- fp = open(sys.argv[2], "w")
- fp.write(ofp.getvalue())
- fp.close()
- ofp.close()
-
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-#! /usr/bin/env python
-
-"""Convert ESIS events to SGML or XML markup.
-
-This is limited, but seems sufficient for the ESIS generated by the
-latex2esis.py script when run over the Python documentation.
-"""
-
-# This should have an explicit option to indicate whether the *INPUT* was
-# generated from an SGML or an XML application.
-
-import errno
-import esistools
-import os
-import re
-import string
-
-from xml.sax.saxutils import escape
-
-
-AUTOCLOSE = ()
-
-EMPTIES_FILENAME = "../sgml/empties.dat"
-LIST_EMPTIES = 0
-
-
-_elem_map = {}
-_attr_map = {}
-_token_map = {}
-
-_normalize_case = str
-
-def map_gi(sgmlgi, map):
- uncased = _normalize_case(sgmlgi)
- try:
- return map[uncased]
- except IndexError:
- map[uncased] = sgmlgi
- return sgmlgi
-
-def null_map_gi(sgmlgi, map):
- return sgmlgi
-
-
-def format_attrs(attrs, xml=0):
- attrs = attrs.items()
- attrs.sort()
- parts = []
- append = parts.append
- for name, value in attrs:
- if xml:
- append('%s="%s"' % (name, escape(value)))
- else:
- # this is a little bogus, but should do for now
- if name == value and isnmtoken(value):
- append(value)
- elif istoken(value):
- if value == "no" + name:
- append(value)
- else:
- append("%s=%s" % (name, value))
- else:
- append('%s="%s"' % (name, escape(value)))
- if parts:
- parts.insert(0, '')
- return string.join(parts)
-
-
-_nmtoken_rx = re.compile("[a-z][-._a-z0-9]*$", re.IGNORECASE)
-def isnmtoken(s):
- return _nmtoken_rx.match(s) is not None
-
-_token_rx = re.compile("[a-z0-9][-._a-z0-9]*$", re.IGNORECASE)
-def istoken(s):
- return _token_rx.match(s) is not None
-
-
-def convert(ifp, ofp, xml=0, autoclose=(), verbatims=()):
- if xml:
- autoclose = ()
- attrs = {}
- lastopened = None
- knownempties = []
- knownempty = 0
- lastempty = 0
- inverbatim = 0
- while 1:
- line = ifp.readline()
- if not line:
- break
-
- type = line[0]
- data = line[1:]
- if data and data[-1] == "\n":
- data = data[:-1]
- if type == "-":
- data = esistools.decode(data)
- data = escape(data)
- if not inverbatim:
- data = string.replace(data, "---", "—")
- ofp.write(data)
- if "\n" in data:
- lastopened = None
- knownempty = 0
- lastempty = 0
- elif type == "(":
- if data == "COMMENT":
- ofp.write("<!--")
- continue
- data = map_gi(data, _elem_map)
- if knownempty and xml:
- ofp.write("<%s%s/>" % (data, format_attrs(attrs, xml)))
- else:
- ofp.write("<%s%s>" % (data, format_attrs(attrs, xml)))
- if knownempty and data not in knownempties:
- # accumulate knowledge!
- knownempties.append(data)
- attrs = {}
- lastopened = data
- lastempty = knownempty
- knownempty = 0
- inverbatim = data in verbatims
- elif type == ")":
- if data == "COMMENT":
- ofp.write("-->")
- continue
- data = map_gi(data, _elem_map)
- if xml:
- if not lastempty:
- ofp.write("</%s>" % data)
- elif data not in knownempties:
- if data in autoclose:
- pass
- elif lastopened == data:
- ofp.write("</>")
- else:
- ofp.write("</%s>" % data)
- lastopened = None
- lastempty = 0
- inverbatim = 0
- elif type == "A":
- name, type, value = string.split(data, " ", 2)
- name = map_gi(name, _attr_map)
- attrs[name] = esistools.decode(value)
- elif type == "e":
- knownempty = 1
- elif type == "&":
- ofp.write("&%s;" % data)
- knownempty = 0
- else:
- raise RuntimeError, "unrecognized ESIS event type: '%s'" % type
-
- if LIST_EMPTIES:
- dump_empty_element_names(knownempties)
-
-
-def dump_empty_element_names(knownempties):
- d = {}
- for gi in knownempties:
- d[gi] = gi
- knownempties.append("")
- if os.path.isfile(EMPTIES_FILENAME):
- fp = open(EMPTIES_FILENAME)
- while 1:
- line = fp.readline()
- if not line:
- break
- gi = string.strip(line)
- if gi:
- d[gi] = gi
- fp = open(EMPTIES_FILENAME, "w")
- gilist = d.keys()
- gilist.sort()
- fp.write(string.join(gilist, "\n"))
- fp.write("\n")
- fp.close()
-
-
-def update_gi_map(map, names, fromsgml=1):
- for name in string.split(names, ","):
- if fromsgml:
- uncased = string.lower(name)
- else:
- uncased = name
- map[uncased] = name
-
-
-def main():
- import getopt
- import sys
- #
- autoclose = AUTOCLOSE
- xml = 1
- xmldecl = 0
- elem_names = ''
- attr_names = ''
- value_names = ''
- verbatims = ('verbatim', 'interactive-session')
- opts, args = getopt.getopt(sys.argv[1:], "adesx",
- ["autoclose=", "declare", "sgml", "xml",
- "elements-map=", "attributes-map",
- "values-map="])
- for opt, arg in opts:
- if opt in ("-d", "--declare"):
- xmldecl = 1
- elif opt == "-e":
- global LIST_EMPTIES
- LIST_EMPTIES = 1
- elif opt in ("-s", "--sgml"):
- xml = 0
- elif opt in ("-x", "--xml"):
- xml = 1
- elif opt in ("-a", "--autoclose"):
- autoclose = string.split(arg, ",")
- elif opt == "--elements-map":
- elem_names = ("%s,%s" % (elem_names, arg))[1:]
- elif opt == "--attributes-map":
- attr_names = ("%s,%s" % (attr_names, arg))[1:]
- elif opt == "--values-map":
- value_names = ("%s,%s" % (value_names, arg))[1:]
- #
- # open input streams:
- #
- if len(args) == 0:
- ifp = sys.stdin
- ofp = sys.stdout
- elif len(args) == 1:
- ifp = open(args[0])
- ofp = sys.stdout
- elif len(args) == 2:
- ifp = open(args[0])
- ofp = open(args[1], "w")
- else:
- usage()
- sys.exit(2)
- #
- # setup the name maps:
- #
- if elem_names or attr_names or value_names:
- # assume the origin was SGML; ignore case of the names from the ESIS
- # stream but set up conversion tables to get the case right on output
- global _normalize_case
- _normalize_case = string.lower
- update_gi_map(_elem_map, string.split(elem_names, ","))
- update_gi_map(_attr_map, string.split(attr_names, ","))
- update_gi_map(_values_map, string.split(value_names, ","))
- else:
- global map_gi
- map_gi = null_map_gi
- #
- # run the conversion:
- #
- try:
- if xml and xmldecl:
- opf.write('<?xml version="1.0" encoding="iso8859-1"?>\n')
- convert(ifp, ofp, xml=xml, autoclose=autoclose, verbatims=verbatims)
- except IOError, (err, msg):
- if err != errno.EPIPE:
- raise
-
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-"""Miscellaneous utility functions useful for dealing with ESIS streams."""
-
-import re
-import string
-
-import xml.dom.pulldom
-
-import xml.sax
-import xml.sax.handler
-import xml.sax.xmlreader
-
-
-_data_match = re.compile(r"[^\\][^\\]*").match
-
-def decode(s):
- r = ''
- while s:
- m = _data_match(s)
- if m:
- r = r + m.group()
- s = s[m.end():]
- elif s[1] == "\\":
- r = r + "\\"
- s = s[2:]
- elif s[1] == "n":
- r = r + "\n"
- s = s[2:]
- elif s[1] == "%":
- s = s[2:]
- n, s = s.split(";", 1)
- r = r + unichr(int(n))
- else:
- raise ValueError, "can't handle " + `s`
- return r
-
-
-_charmap = {}
-for c in map(chr, range(256)):
- _charmap[c] = c
-_charmap["\n"] = r"\n"
-_charmap["\\"] = r"\\"
-del c
-
-_null_join = ''.join
-def encode(s):
- return _null_join(map(_charmap.get, s))
-
-
-class ESISReader(xml.sax.xmlreader.XMLReader):
- """SAX Reader which reads from an ESIS stream.
-
- No verification of the document structure is performed by the
- reader; a general verifier could be used as the target
- ContentHandler instance.
-
- """
- _decl_handler = None
- _lexical_handler = None
-
- _public_id = None
- _system_id = None
-
- _buffer = ""
- _is_empty = 0
- _lineno = 0
- _started = 0
-
- def __init__(self, contentHandler=None, errorHandler=None):
- xml.sax.xmlreader.XMLReader.__init__(self)
- self._attrs = {}
- self._attributes = Attributes(self._attrs)
- self._locator = Locator()
- self._empties = {}
- if contentHandler:
- self.setContentHandler(contentHandler)
- if errorHandler:
- self.setErrorHandler(errorHandler)
-
- def get_empties(self):
- return self._empties.keys()
-
- #
- # XMLReader interface
- #
-
- def parse(self, source):
- raise RuntimeError
- self._locator._public_id = source.getPublicId()
- self._locator._system_id = source.getSystemId()
- fp = source.getByteStream()
- handler = self.getContentHandler()
- if handler:
- handler.startDocument()
- lineno = 0
- while 1:
- token, data = self._get_token(fp)
- if token is None:
- break
- lineno = lineno + 1
- self._locator._lineno = lineno
- self._handle_token(token, data)
- handler = self.getContentHandler()
- if handler:
- handler.startDocument()
-
- def feed(self, data):
- if not self._started:
- handler = self.getContentHandler()
- if handler:
- handler.startDocument()
- self._started = 1
- data = self._buffer + data
- self._buffer = None
- lines = data.split("\n")
- if lines:
- for line in lines[:-1]:
- self._lineno = self._lineno + 1
- self._locator._lineno = self._lineno
- if not line:
- e = xml.sax.SAXParseException(
- "ESIS input line contains no token type mark",
- None, self._locator)
- self.getErrorHandler().error(e)
- else:
- self._handle_token(line[0], line[1:])
- self._buffer = lines[-1]
- else:
- self._buffer = ""
-
- def close(self):
- handler = self.getContentHandler()
- if handler:
- handler.endDocument()
- self._buffer = ""
-
- def _get_token(self, fp):
- try:
- line = fp.readline()
- except IOError, e:
- e = SAXException("I/O error reading input stream", e)
- self.getErrorHandler().fatalError(e)
- return
- if not line:
- return None, None
- if line[-1] == "\n":
- line = line[:-1]
- if not line:
- e = xml.sax.SAXParseException(
- "ESIS input line contains no token type mark",
- None, self._locator)
- self.getErrorHandler().error(e)
- return
- return line[0], line[1:]
-
- def _handle_token(self, token, data):
- handler = self.getContentHandler()
- if token == '-':
- if data and handler:
- handler.characters(decode(data))
- elif token == ')':
- if handler:
- handler.endElement(decode(data))
- elif token == '(':
- if self._is_empty:
- self._empties[data] = 1
- if handler:
- handler.startElement(data, self._attributes)
- self._attrs.clear()
- self._is_empty = 0
- elif token == 'A':
- name, value = data.split(' ', 1)
- if value != "IMPLIED":
- type, value = value.split(' ', 1)
- self._attrs[name] = (decode(value), type)
- elif token == '&':
- # entity reference in SAX?
- pass
- elif token == '?':
- if handler:
- if ' ' in data:
- target, data = string.split(data, None, 1)
- else:
- target, data = data, ""
- handler.processingInstruction(target, decode(data))
- elif token == 'N':
- handler = self.getDTDHandler()
- if handler:
- handler.notationDecl(data, self._public_id, self._system_id)
- self._public_id = None
- self._system_id = None
- elif token == 'p':
- self._public_id = decode(data)
- elif token == 's':
- self._system_id = decode(data)
- elif token == 'e':
- self._is_empty = 1
- elif token == 'C':
- pass
- else:
- e = SAXParseException("unknown ESIS token in event stream",
- None, self._locator)
- self.getErrorHandler().error(e)
-
- def setContentHandler(self, handler):
- old = self.getContentHandler()
- if old:
- old.setDocumentLocator(None)
- if handler:
- handler.setDocumentLocator(self._locator)
- xml.sax.xmlreader.XMLReader.setContentHandler(self, handler)
-
- def getProperty(self, property):
- if property == xml.sax.handler.property_lexical_handler:
- return self._lexical_handler
-
- elif property == xml.sax.handler.property_declaration_handler:
- return self._decl_handler
-
- else:
- raise xml.sax.SAXNotRecognizedException("unknown property %s"
- % `property`)
-
- def setProperty(self, property, value):
- if property == xml.sax.handler.property_lexical_handler:
- if self._lexical_handler:
- self._lexical_handler.setDocumentLocator(None)
- if value:
- value.setDocumentLocator(self._locator)
- self._lexical_handler = value
-
- elif property == xml.sax.handler.property_declaration_handler:
- if self._decl_handler:
- self._decl_handler.setDocumentLocator(None)
- if value:
- value.setDocumentLocator(self._locator)
- self._decl_handler = value
-
- else:
- raise xml.sax.SAXNotRecognizedException()
-
- def getFeature(self, feature):
- if feature == xml.sax.handler.feature_namespaces:
- return 1
- else:
- return xml.sax.xmlreader.XMLReader.getFeature(self, feature)
-
- def setFeature(self, feature, enabled):
- if feature == xml.sax.handler.feature_namespaces:
- pass
- else:
- xml.sax.xmlreader.XMLReader.setFeature(self, feature, enabled)
-
-
-class Attributes(xml.sax.xmlreader.AttributesImpl):
- # self._attrs has the form {name: (value, type)}
-
- def getType(self, name):
- return self._attrs[name][1]
-
- def getValue(self, name):
- return self._attrs[name][0]
-
- def getValueByQName(self, name):
- return self._attrs[name][0]
-
- def __getitem__(self, name):
- return self._attrs[name][0]
-
- def get(self, name, default=None):
- if self._attrs.has_key(name):
- return self._attrs[name][0]
- return default
-
- def items(self):
- L = []
- for name, (value, type) in self._attrs.items():
- L.append((name, value))
- return L
-
- def values(self):
- L = []
- for value, type in self._attrs.values():
- L.append(value)
- return L
-
-
-class Locator(xml.sax.xmlreader.Locator):
- _lineno = -1
- _public_id = None
- _system_id = None
-
- def getLineNumber(self):
- return self._lineno
-
- def getPublicId(self):
- return self._public_id
-
- def getSystemId(self):
- return self._system_id
-
-
-def parse(stream_or_string, parser=None):
- if type(stream_or_string) in [type(""), type(u"")]:
- stream = open(stream_or_string)
- else:
- stream = stream_or_string
- if not parser:
- parser = ESISReader()
- return xml.dom.pulldom.DOMEventStream(stream, parser, (2 ** 14) - 20)
+++ /dev/null
-#! /usr/bin/env python
-
-"""Generate ESIS events based on a LaTeX source document and
-configuration data.
-
-The conversion is not strong enough to work with arbitrary LaTeX
-documents; it has only been designed to work with the highly stylized
-markup used in the standard Python documentation. A lot of
-information about specific markup is encoded in the control table
-passed to the convert() function; changing this table can allow this
-tool to support additional LaTeX markups.
-
-The format of the table is largely undocumented; see the commented
-headers where the table is specified in main(). There is no provision
-to load an alternate table from an external file.
-"""
-
-import errno
-import getopt
-import os
-import re
-import string
-import sys
-import UserList
-import xml.sax.saxutils
-
-from types import ListType, StringType, TupleType
-
-try:
- from xml.parsers.xmllib import XMLParser
-except ImportError:
- from xmllib import XMLParser
-
-
-from esistools import encode
-
-
-DEBUG = 0
-
-
-class LaTeXFormatError(Exception):
- pass
-
-
-class LaTeXStackError(LaTeXFormatError):
- def __init__(self, found, stack):
- msg = "environment close for %s doesn't match;\n stack = %s" \
- % (found, stack)
- self.found = found
- self.stack = stack[:]
- LaTeXFormatError.__init__(self, msg)
-
-\f
-_begin_env_rx = re.compile(r"[\\]begin{([^}]*)}")
-_end_env_rx = re.compile(r"[\\]end{([^}]*)}")
-_begin_macro_rx = re.compile(r"[\\]([a-zA-Z]+[*]?) ?({|\s*\n?)")
-_comment_rx = re.compile("%+ ?(.*)\n[ \t]*")
-_text_rx = re.compile(r"[^]~%\\{}]+")
-_optional_rx = re.compile(r"\s*[[]([^]]*)[]]")
-# _parameter_rx is this complicated to allow {...} inside a parameter;
-# this is useful to match tabular layout specifications like {c|p{24pt}}
-_parameter_rx = re.compile("[ \n]*{(([^{}}]|{[^}]*})*)}")
-_token_rx = re.compile(r"[a-zA-Z][a-zA-Z0-9.-]*$")
-_start_group_rx = re.compile("[ \n]*{")
-_start_optional_rx = re.compile("[ \n]*[[]")
-
-
-ESCAPED_CHARS = "$%#^ {}&~"
-
-
-def dbgmsg(msg):
- if DEBUG:
- sys.stderr.write(msg + "\n")
-
-def pushing(name, point, depth):
- dbgmsg("pushing <%s> at %s" % (name, point))
-
-def popping(name, point, depth):
- dbgmsg("popping </%s> at %s" % (name, point))
-
-
-class _Stack(UserList.UserList):
- def append(self, entry):
- if type(entry) is not StringType:
- raise LaTeXFormatError("cannot push non-string on stack: "
- + `entry`)
- #dbgmsg("%s<%s>" % (" "*len(self.data), entry))
- self.data.append(entry)
-
- def pop(self, index=-1):
- entry = self.data[index]
- del self.data[index]
- #dbgmsg("%s</%s>" % (" "*len(self.data), entry))
-
- def __delitem__(self, index):
- entry = self.data[index]
- del self.data[index]
- #dbgmsg("%s</%s>" % (" "*len(self.data), entry))
-
-
-def new_stack():
- if DEBUG:
- return _Stack()
- return []
-
-\f
-class Conversion:
- def __init__(self, ifp, ofp, table):
- self.write = ofp.write
- self.ofp = ofp
- self.table = table
- self.line = string.join(map(string.rstrip, ifp.readlines()), "\n")
- self.preamble = 1
-
- def convert(self):
- self.subconvert()
-
- def subconvert(self, endchar=None, depth=0):
- #
- # Parses content, including sub-structures, until the character
- # 'endchar' is found (with no open structures), or until the end
- # of the input data is endchar is None.
- #
- stack = new_stack()
- line = self.line
- while line:
- if line[0] == endchar and not stack:
- self.line = line
- return line
- m = _comment_rx.match(line)
- if m:
- text = m.group(1)
- if text:
- self.write("(COMMENT\n- %s \n)COMMENT\n-\\n\n"
- % encode(text))
- line = line[m.end():]
- continue
- m = _begin_env_rx.match(line)
- if m:
- name = m.group(1)
- entry = self.get_env_entry(name)
- # re-write to use the macro handler
- line = r"\%s %s" % (name, line[m.end():])
- continue
- m = _end_env_rx.match(line)
- if m:
- # end of environment
- envname = m.group(1)
- entry = self.get_entry(envname)
- while stack and envname != stack[-1] \
- and stack[-1] in entry.endcloses:
- self.write(")%s\n" % stack.pop())
- if stack and envname == stack[-1]:
- self.write(")%s\n" % entry.outputname)
- del stack[-1]
- else:
- raise LaTeXStackError(envname, stack)
- line = line[m.end():]
- continue
- m = _begin_macro_rx.match(line)
- if m:
- # start of macro
- macroname = m.group(1)
- if macroname == "c":
- # Ugh! This is a combining character...
- endpos = m.end()
- self.combining_char("c", line[endpos])
- line = line[endpos + 1:]
- continue
- entry = self.get_entry(macroname)
- if entry.verbatim:
- # magic case!
- pos = string.find(line, "\\end{%s}" % macroname)
- text = line[m.end(1):pos]
- stack.append(entry.name)
- self.write("(%s\n" % entry.outputname)
- self.write("-%s\n" % encode(text))
- self.write(")%s\n" % entry.outputname)
- stack.pop()
- line = line[pos + len("\\end{%s}" % macroname):]
- continue
- while stack and stack[-1] in entry.closes:
- top = stack.pop()
- topentry = self.get_entry(top)
- if topentry.outputname:
- self.write(")%s\n-\\n\n" % topentry.outputname)
- #
- if entry.outputname:
- if entry.empty:
- self.write("e\n")
- #
- params, optional, empty, environ = self.start_macro(macroname)
- # rip off the macroname
- if params:
- line = line[m.end(1):]
- elif empty:
- line = line[m.end(1):]
- else:
- line = line[m.end():]
- opened = 0
- implied_content = 0
-
- # handle attribute mappings here:
- for pentry in params:
- if pentry.type == "attribute":
- if pentry.optional:
- m = _optional_rx.match(line)
- if m and entry.outputname:
- line = line[m.end():]
- self.dump_attr(pentry, m.group(1))
- elif pentry.text and entry.outputname:
- # value supplied by conversion spec:
- self.dump_attr(pentry, pentry.text)
- else:
- m = _parameter_rx.match(line)
- if not m:
- raise LaTeXFormatError(
- "could not extract parameter %s for %s: %s"
- % (pentry.name, macroname, `line[:100]`))
- if entry.outputname:
- self.dump_attr(pentry, m.group(1))
- line = line[m.end():]
- elif pentry.type == "child":
- if pentry.optional:
- m = _optional_rx.match(line)
- if m:
- line = line[m.end():]
- if entry.outputname and not opened:
- opened = 1
- self.write("(%s\n" % entry.outputname)
- stack.append(macroname)
- stack.append(pentry.name)
- self.write("(%s\n" % pentry.name)
- self.write("-%s\n" % encode(m.group(1)))
- self.write(")%s\n" % pentry.name)
- stack.pop()
- else:
- if entry.outputname and not opened:
- opened = 1
- self.write("(%s\n" % entry.outputname)
- stack.append(entry.name)
- self.write("(%s\n" % pentry.name)
- stack.append(pentry.name)
- self.line = skip_white(line)[1:]
- line = self.subconvert(
- "}", len(stack) + depth + 1)[1:]
- self.write(")%s\n" % stack.pop())
- elif pentry.type == "content":
- if pentry.implied:
- implied_content = 1
- else:
- if entry.outputname and not opened:
- opened = 1
- self.write("(%s\n" % entry.outputname)
- stack.append(entry.name)
- line = skip_white(line)
- if line[0] != "{":
- raise LaTeXFormatError(
- "missing content for " + macroname)
- self.line = line[1:]
- line = self.subconvert("}", len(stack) + depth + 1)
- if line and line[0] == "}":
- line = line[1:]
- elif pentry.type == "text" and pentry.text:
- if entry.outputname and not opened:
- opened = 1
- stack.append(entry.name)
- self.write("(%s\n" % entry.outputname)
- #dbgmsg("--- text: %s" % `pentry.text`)
- self.write("-%s\n" % encode(pentry.text))
- elif pentry.type == "entityref":
- self.write("&%s\n" % pentry.name)
- if entry.outputname:
- if not opened:
- self.write("(%s\n" % entry.outputname)
- stack.append(entry.name)
- if not implied_content:
- self.write(")%s\n" % entry.outputname)
- stack.pop()
- continue
- if line[0] == endchar and not stack:
- self.line = line[1:]
- return self.line
- if line[0] == "}":
- # end of macro or group
- macroname = stack[-1]
- if macroname:
- conversion = self.table[macroname]
- if conversion.outputname:
- # otherwise, it was just a bare group
- self.write(")%s\n" % conversion.outputname)
- del stack[-1]
- line = line[1:]
- continue
- if line[0] == "~":
- # don't worry about the "tie" aspect of this command
- line = line[1:]
- self.write("- \n")
- continue
- if line[0] == "{":
- stack.append("")
- line = line[1:]
- continue
- if line[0] == "\\" and line[1] in ESCAPED_CHARS:
- self.write("-%s\n" % encode(line[1]))
- line = line[2:]
- continue
- if line[:2] == r"\\":
- self.write("(BREAK\n)BREAK\n")
- line = line[2:]
- continue
- if line[:2] == r"\_":
- line = "_" + line[2:]
- continue
- if line[:2] in (r"\'", r'\"'):
- # combining characters...
- self.combining_char(line[1], line[2])
- line = line[3:]
- continue
- m = _text_rx.match(line)
- if m:
- text = encode(m.group())
- self.write("-%s\n" % text)
- line = line[m.end():]
- continue
- # special case because of \item[]
- # XXX can we axe this???
- if line[0] == "]":
- self.write("-]\n")
- line = line[1:]
- continue
- # avoid infinite loops
- extra = ""
- if len(line) > 100:
- extra = "..."
- raise LaTeXFormatError("could not identify markup: %s%s"
- % (`line[:100]`, extra))
- while stack:
- entry = self.get_entry(stack[-1])
- if entry.closes:
- self.write(")%s\n-%s\n" % (entry.outputname, encode("\n")))
- del stack[-1]
- else:
- break
- if stack:
- raise LaTeXFormatError("elements remain on stack: "
- + string.join(stack, ", "))
- # otherwise we just ran out of input here...
-
- # This is a really limited table of combinations, but it will have
- # to do for now.
- _combinations = {
- ("c", "c"): 0x00E7,
- ("'", "e"): 0x00E9,
- ('"', "o"): 0x00F6,
- }
-
- def combining_char(self, prefix, char):
- ordinal = self._combinations[(prefix, char)]
- self.write("-\\%%%d;\n" % ordinal)
-
- def start_macro(self, name):
- conversion = self.get_entry(name)
- parameters = conversion.parameters
- optional = parameters and parameters[0].optional
- return parameters, optional, conversion.empty, conversion.environment
-
- def get_entry(self, name):
- entry = self.table.get(name)
- if entry is None:
- dbgmsg("get_entry(%s) failing; building default entry!" % `name`)
- # not defined; build a default entry:
- entry = TableEntry(name)
- entry.has_content = 1
- entry.parameters.append(Parameter("content"))
- self.table[name] = entry
- return entry
-
- def get_env_entry(self, name):
- entry = self.table.get(name)
- if entry is None:
- # not defined; build a default entry:
- entry = TableEntry(name, 1)
- entry.has_content = 1
- entry.parameters.append(Parameter("content"))
- entry.parameters[-1].implied = 1
- self.table[name] = entry
- elif not entry.environment:
- raise LaTeXFormatError(
- name + " is defined as a macro; expected environment")
- return entry
-
- def dump_attr(self, pentry, value):
- if not (pentry.name and value):
- return
- if _token_rx.match(value):
- dtype = "TOKEN"
- else:
- dtype = "CDATA"
- self.write("A%s %s %s\n" % (pentry.name, dtype, encode(value)))
-
-
-def convert(ifp, ofp, table):
- c = Conversion(ifp, ofp, table)
- try:
- c.convert()
- except IOError, (err, msg):
- if err != errno.EPIPE:
- raise
-
-
-def skip_white(line):
- while line and line[0] in " %\n\t\r":
- line = string.lstrip(line[1:])
- return line
-
-
-
-class TableEntry:
- def __init__(self, name, environment=0):
- self.name = name
- self.outputname = name
- self.environment = environment
- self.empty = not environment
- self.has_content = 0
- self.verbatim = 0
- self.auto_close = 0
- self.parameters = []
- self.closes = []
- self.endcloses = []
-
-class Parameter:
- def __init__(self, type, name=None, optional=0):
- self.type = type
- self.name = name
- self.optional = optional
- self.text = ''
- self.implied = 0
-
-
-class TableParser(XMLParser):
- def __init__(self, table=None):
- if table is None:
- table = {}
- self.__table = table
- self.__current = None
- self.__buffer = ''
- XMLParser.__init__(self)
-
- def get_table(self):
- for entry in self.__table.values():
- if entry.environment and not entry.has_content:
- p = Parameter("content")
- p.implied = 1
- entry.parameters.append(p)
- entry.has_content = 1
- return self.__table
-
- def start_environment(self, attrs):
- name = attrs["name"]
- self.__current = TableEntry(name, environment=1)
- self.__current.verbatim = attrs.get("verbatim") == "yes"
- if attrs.has_key("outputname"):
- self.__current.outputname = attrs.get("outputname")
- self.__current.endcloses = string.split(attrs.get("endcloses", ""))
- def end_environment(self):
- self.end_macro()
-
- def start_macro(self, attrs):
- name = attrs["name"]
- self.__current = TableEntry(name)
- self.__current.closes = string.split(attrs.get("closes", ""))
- if attrs.has_key("outputname"):
- self.__current.outputname = attrs.get("outputname")
- def end_macro(self):
- self.__table[self.__current.name] = self.__current
- self.__current = None
-
- def start_attribute(self, attrs):
- name = attrs.get("name")
- optional = attrs.get("optional") == "yes"
- if name:
- p = Parameter("attribute", name, optional=optional)
- else:
- p = Parameter("attribute", optional=optional)
- self.__current.parameters.append(p)
- self.__buffer = ''
- def end_attribute(self):
- self.__current.parameters[-1].text = self.__buffer
-
- def start_entityref(self, attrs):
- name = attrs["name"]
- p = Parameter("entityref", name)
- self.__current.parameters.append(p)
-
- def start_child(self, attrs):
- name = attrs["name"]
- p = Parameter("child", name, attrs.get("optional") == "yes")
- self.__current.parameters.append(p)
- self.__current.empty = 0
-
- def start_content(self, attrs):
- p = Parameter("content")
- p.implied = attrs.get("implied") == "yes"
- if self.__current.environment:
- p.implied = 1
- self.__current.parameters.append(p)
- self.__current.has_content = 1
- self.__current.empty = 0
-
- def start_text(self, attrs):
- self.__current.empty = 0
- self.__buffer = ''
- def end_text(self):
- p = Parameter("text")
- p.text = self.__buffer
- self.__current.parameters.append(p)
-
- def handle_data(self, data):
- self.__buffer = self.__buffer + data
-
-
-def load_table(fp, table=None):
- parser = TableParser(table=table)
- parser.feed(fp.read())
- parser.close()
- return parser.get_table()
-
-
-def main():
- global DEBUG
- #
- opts, args = getopt.getopt(sys.argv[1:], "D", ["debug"])
- for opt, arg in opts:
- if opt in ("-D", "--debug"):
- DEBUG = DEBUG + 1
- if len(args) == 0:
- ifp = sys.stdin
- ofp = sys.stdout
- elif len(args) == 1:
- ifp = open(args)
- ofp = sys.stdout
- elif len(args) == 2:
- ifp = open(args[0])
- ofp = open(args[1], "w")
- else:
- usage()
- sys.exit(2)
-
- table = load_table(open(os.path.join(sys.path[0], 'conversion.xml')))
- convert(ifp, ofp, table)
-
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-# -*- makefile -*-
-#
-# Extra magic needed by the LaTeX->XML conversion process. This requires
-# $(TOOLSDIR) to be properly defined.
-
-DOCFIXER= $(TOOLSDIR)/sgmlconv/docfixer.py
-ESIS2ML= $(TOOLSDIR)/sgmlconv/esis2sgml.py
-LATEX2ESIS= $(TOOLSDIR)/sgmlconv/latex2esis.py
-CONVERSION= $(TOOLSDIR)/sgmlconv/conversion.xml
-
-ESISTARGETS= $(patsubst %.tex,%.esis,$(wildcard *.tex))
-ESIS1TARGETS= $(patsubst %.tex,%.esis1,$(wildcard *.tex))
-XMLTARGETS= $(patsubst %.tex,%.xml,$(wildcard *.tex))
-
-L2EFLAGS=
-
-all: xml
-
-esis: $(ESISTARGETS)
-esis1: $(ESIS1TARGETS)
-xml: $(XMLTARGETS)
-
-ESISTOOLS= $(TOOLSDIR)/sgmlconv/esistools.py
-
-$(ESISTARGETS): $(LATEX2ESIS) $(DOCFIXER) $(ESISTOOLS) $(CONVERSION)
-$(ESIS1TARGETS): $(LATEX2ESIS) $(CONVERSION)
-# This variant is easier to work with while debugging the conversion spec:
-#$(ESISTARGETS): $(LATEX2ESIS) $(DOCFIXER) $(ESISTOOLS)
-$(XMLTARGETS): $(ESIS2ML)
-
-
-.SUFFIXES: .esis .esis1 .tex .xml
-
-.tex.esis1:
- $(LATEX2ESIS) $(L2EFLAGS) $< $@
-
-.esis1.esis:
- $(DOCFIXER) $< $@
-
-.esis.xml:
- $(ESIS2ML) --xml $< $@
-
-
-clean:
- rm -f *.esis *.esis1
-
-clobber: clean
- rm -f *.xml
+++ /dev/null
-"""Miscellaneous support code shared by some of the tool scripts.
-
-This includes option parsing code, HTML formatting code, and a couple of
-useful helpers.
-
-"""
-__version__ = '$Revision: 1.1.1.1 $'
-
-
-import getopt
-import string
-import sys
-
-
-class Options:
- __short_args = "a:c:ho:"
- __long_args = [
- # script controls
- "columns=", "help", "output=",
-
- # content components
- "address=", "iconserver=",
- "title=", "uplink=", "uptitle="]
-
- outputfile = "-"
- columns = 1
- letters = 0
- uplink = "./"
- uptitle = "Python Documentation Index"
-
- def __init__(self):
- self.args = []
- self.variables = {"address": "",
- "iconserver": "icons",
- "imgtype": "gif",
- "title": "Global Module Index",
- }
-
- def add_args(self, short=None, long=None):
- if short:
- self.__short_args = self.__short_args + short
- if long:
- self.__long_args = self.__long_args + long
-
- def parse(self, args):
- try:
- opts, args = getopt.getopt(args, self.__short_args,
- self.__long_args)
- except getopt.error:
- sys.stdout = sys.stderr
- self.usage()
- sys.exit(2)
- self.args = self.args + args
- for opt, val in opts:
- if opt in ("-a", "--address"):
- val = string.strip(val)
- if val:
- val = "<address>\n%s\n</address>\n" % val
- self.variables["address"] = val
- elif opt in ("-h", "--help"):
- self.usage()
- sys.exit()
- elif opt in ("-o", "--output"):
- self.outputfile = val
- elif opt in ("-c", "--columns"):
- self.columns = int(val)
- elif opt == "--title":
- self.variables["title"] = val.strip()
- elif opt == "--uplink":
- self.uplink = val.strip()
- elif opt == "--uptitle":
- self.uptitle = val.strip()
- elif opt == "--iconserver":
- self.variables["iconserver"] = val.strip() or "."
- else:
- self.handle_option(opt, val)
- if self.uplink and self.uptitle:
- self.variables["uplinkalt"] = "up"
- self.variables["uplinkicon"] = "up"
- else:
- self.variables["uplinkalt"] = ""
- self.variables["uplinkicon"] = "blank"
- self.variables["uplink"] = self.uplink
- self.variables["uptitle"] = self.uptitle
-
- def handle_option(self, opt, val):
- raise getopt.error("option %s not recognized" % opt)
-
- def get_header(self):
- return HEAD % self.variables
-
- def get_footer(self):
- return TAIL % self.variables
-
- def get_output_file(self, filename=None):
- if filename is None:
- filename = self.outputfile
- if filename == "-":
- return sys.stdout
- else:
- return open(filename, "w")
-
-
-NAVIGATION = '''\
-<div class="navigation">
-<table width="100%%" cellpadding="0" cellspacing="2">
-<tr>
-<td><img width="32" height="32" align="bottom" border="0" alt=""
- src="%(iconserver)s/blank.%(imgtype)s"></td>
-<td><a href="%(uplink)s"
- title="%(uptitle)s"><img width="32" height="32" align="bottom" border="0"
- alt="%(uplinkalt)s"
- src="%(iconserver)s/%(uplinkicon)s.%(imgtype)s"></a></td>
-<td><img width="32" height="32" align="bottom" border="0" alt=""
- src="%(iconserver)s/blank.%(imgtype)s"></td>
-<td align="center" width="100%%">%(title)s</td>
-<td><img width="32" height="32" align="bottom" border="0" alt=""
- src="%(iconserver)s/blank.%(imgtype)s"></td>
-<td><img width="32" height="32" align="bottom" border="0" alt=""
- src="%(iconserver)s/blank.%(imgtype)s"></td>
-<td><img width="32" height="32" align="bottom" border="0" alt=""
- src="%(iconserver)s/blank.%(imgtype)s"></td>
-</tr></table>
-<b class="navlabel">Up:</b> <span class="sectref"><a href="%(uplink)s"
- title="%(uptitle)s">%(uptitle)s</A></span>
-<br></div>
-'''
-
-HEAD = '''\
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<html>
-<head>
- <title>%(title)s</title>
- <meta name="description" content="%(title)s">
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <link rel="STYLESHEET" href="lib/lib.css">
-</head>
-<body>
-''' + NAVIGATION + '''\
-<hr>
-
-<h2>%(title)s</h2>
-
-'''
-
-TAIL = "<hr>\n" + NAVIGATION + '''\
-%(address)s</body>
-</html>
-'''
+++ /dev/null
-\documentclass{howto}
-
-% This is a template for short or medium-size Python-related documents,
-% mostly notably the series of HOWTOs, but it can be used for any
-% document you like.
-
-% The title should be descriptive enough for people to be able to find
-% the relevant document.
-\title{Spammifying Sprockets in Python}
-
-% Increment the release number whenever significant changes are made.
-% The author and/or editor can define 'significant' however they like.
-\release{0.00}
-
-% At minimum, give your name and an e-mail address. You can include a
-% snail-mail address if you like.
-\author{Me, 'cause I wrote it}
-\authoraddress{Me, 'cause I'm self-employed.}
-
-\begin{document}
-\maketitle
-
-% This makes the Abstract go on a separate page in the HTML version;
-% if a copyright notice is used, it should go immediately after this.
-%
-\ifhtml
-\chapter*{Front Matter\label{front}}
-\fi
-
-% Copyright statement should go here, if needed.
-% ...
-
-% The abstract should be a paragraph or two long, and describe the
-% scope of the document.
-\begin{abstract}
-\noindent
-This document describes how to spammify sprockets. It is a useful
-example of a Python HOWTO document. It is not dependent on any
-particular sprocket implementation, and includes a Python-based
-implementation in the \module{sprunkit} module.
-\end{abstract}
-
-\tableofcontents
-
-Spammifying sprockets from Python is both fun and entertaining.
-Applying the techniques described here, you can also fill your hard
-disk quite effectively.
-
-\section{What is Sprocket Spammification?}
-
-You have to ask? It's the only thing to do to your sprockets!
-
-
-\section{Why Use Python?}
-
-Python is an excellent language from which to spammify your sprockets
-since you can do it on any platform.
-
-
-\section{Software Requirements}
-
-You need to have the following software installed:
-
-% The {itemize} environment uses a bullet for each \item. If you want the
-% \item's numbered, use the {enumerate} environment instead.
-\begin{itemize}
- \item Python 1.9.
- \item Some sprocket definition files.
- \item At least one sprocket system implementation.
-\end{itemize}
-
-Note that the \module{sprunkit} is provided with this package and
-implements ActiveSprockets in Python.
-
-
-% The preceding sections will have been written in a gentler,
-% introductory style. You may also wish to include a reference
-% section, documenting all the functions/exceptions/constants.
-% Often, these will be placed in separate files and input like this:
-
-\input{module}
-
-
-\appendix
-
-\section{This is an Appendix}
-
-To create an appendix in a Python HOWTO document, use markup like
-this:
-
-\begin{verbatim}
-\appendix
-
-\section{This is an Appendix}
-
-To create an appendix in a Python HOWTO document, ....
-
-
-\section{This is another}
-
-Just add another \section{}, but don't say \appendix again.
-\end{verbatim}
-
-
-\end{document}
+++ /dev/null
-\documentclass{manual}
-
-\title{Big Python Manual}
-
-\author{Your Name Here}
-
-% Please at least include a long-lived email address;
-% the rest is at your discretion.
-\authoraddress{
- Organization name, if applicable \\
- Street address, if you want to use it \\
- E-mail: \email{your-email@your.domain}
-}
-
-\date{April 30, 1999} % update before release!
- % Use an explicit date so that reformatting
- % doesn't cause a new date to be used. Setting
- % the date to \today can be used during draft
- % stages to make it easier to handle versions.
-
-\release{x.y} % release version; this is used to define the
- % \version macro
-
-\makeindex % tell \index to actually write the .idx file
-\makemodindex % If this contains a lot of module sections.
-
-
-\begin{document}
-
-\maketitle
-
-% This makes the contents more accessible from the front page of the HTML.
-\ifhtml
-\chapter*{Front Matter\label{front}}
-\fi
-
-%\input{copyright}
-
-\begin{abstract}
-
-\noindent
-Big Python is a special version of Python for users who require larger
-keys on their keyboards. It accomodates their special needs by ...
-
-\end{abstract}
-
-\tableofcontents
-
-
-\chapter{...}
-
-My chapter.
-
-
-\appendix
-\chapter{...}
-
-My appendix.
-
-The \code{\e appendix} markup need not be repeated for additional
-appendices.
-
-
-%
-% The ugly "%begin{latexonly}" pseudo-environments are really just to
-% keep LaTeX2HTML quiet during the \renewcommand{} macros; they're
-% not really valuable.
-%
-% If you don't want the Module Index, you can remove all of this up
-% until the second \input line.
-%
-%begin{latexonly}
-\renewcommand{\indexname}{Module Index}
-%end{latexonly}
-\input{mod\jobname.ind} % Module Index
-
-%begin{latexonly}
-\renewcommand{\indexname}{Index}
-%end{latexonly}
-\input{\jobname.ind} % Index
-
-\end{document}
+++ /dev/null
-% Template for a library manual section.
-% PLEASE REMOVE THE COMMENTS AFTER USING THE TEMPLATE
-
-% ==== 0. ====
-% Copy this file to <mydir>/lib<mymodule>.tex, and edit that file
-% according to the instructions below.
-
-
-% ==== 1. ====
-% The section prologue. Give the section a title and provide some
-% meta-information. References to the module should use
-% \refbimodindex, \refstmodindex, \refexmodindex or \refmodindex, as
-% appropriate.
-
-\section{\module{spam} ---
- Short descrition, for section title}
-
-% Choose one of these to specify the module module name. If there's
-% an underscore in the name, use
-% \declaremodule[modname]{...}{mod_name} instead.
-%
-\declaremodule{builtin}{spam} % standard library, in C
-\declaremodule{standard}{spam} % standard library, in Python
-\declaremodule{extension}{spam} % not standard, in C
-\declaremodule{}{spam} % not standard, in Python
-
-% Portability statement: Uncomment and fill in the parameter to specify the
-% availability of the module. The parameter can be Unix, IRIX, SunOS, Mac,
-% Windows, or lots of other stuff. When ``Mac'' is specified, the availability
-% statement will say ``Macintosh'' and the Module Index may say ``Mac''.
-% Please use a name that has already been used whenever applicable. If this
-% is omitted, no availability statement is produced or implied.
-%
-% \platform{UNIX}
-
-% These apply to all modules:
-
-\moduleauthor{name}{email} % Author of the module code;
- % omit if not known.
-\sectionauthor{name}{email} % Author of the documentation,
- % even if not a module section.
-
-
-% Leave at least one blank line after this, to simplify ad-hoc tools
-% that are sometimes used to massage these files.
-\modulesynopsis{This is a one-line descrition, for the chapter header.}
-
-
-% ==== 2. ====
-% Give a short overview of what the module does.
-% If it is platform specific, mention this.
-% Mention other important restrictions or general operating principles.
-% For example:
-
-The \module{spam} module defines operations for handling cans of Spam.
-It knows the four generally available Spam varieties and understands
-both can sizes.
-
-Because spamification requires \UNIX{} process management, the module
-is only available on genuine \UNIX{} systems.
-
-
-% ==== 3. ====
-% List the public functions defined by the module. Begin with a
-% standard phrase. You may also list the exceptions and other data
-% items defined in the module, insofar as they are important for the
-% user.
-
-The \module{spam} module defines the following functions:
-
-% ---- 3.1. ----
-% For each function, use a ``funcdesc'' block. This has exactly two
-% parameters (each parameters is contained in a set of curly braces):
-% the first parameter is the function name (this automatically
-% generates an index entry); the second parameter is the function's
-% argument list. If there are no arguments, use an empty pair of
-% curly braces. If there is more than one argument, separate the
-% arguments with backslash-comma. Optional parts of the parameter
-% list are contained in \optional{...} (this generates a set of square
-% brackets around its parameter). Arguments are automatically set in
-% italics in the parameter list. Each argument should be mentioned at
-% least once in the description; each usage (even inside \code{...})
-% should be enclosed in \var{...}.
-
-\begin{funcdesc}{open}{filename\optional{, mode\optional{, buffersize}}}
-Open the file \var{filename} as a can of Spam. The optional
-\var{mode} and \var{buffersize} arguments specify the read/write mode
-(\code{'r'} (default) or \code{'w'}) and the buffer size (default:
-system dependent).
-\end{funcdesc}
-
-% ---- 3.2. ----
-% Data items are described using a ``datadesc'' block. This has only
-% one parameter: the item's name.
-
-\begin{datadesc}{cansize}
-The default can size, in ounces. Legal values are 7 and 12. The
-default varies per supermarket. This variable should not be changed
-once the \function{open()} function has been called.
-\end{datadesc}
-
-% --- 3.3. ---
-% Exceptions are described using a ``excdesc'' block. This has only
-% one parameter: the exception name. Exceptions defined as classes in
-% the source code should be documented using this environment, but
-% constructor parameters must be ommitted.
-
-\begin{excdesc}{error}
-Exception raised when an operation fails for a Spam specific reason.
-The exception argument is a string describing the reason of the
-failure.
-\end{excdesc}
-
-% ---- 3.4. ----
-% Other standard environments:
-%
-% classdesc - Python classes; same arguments are funcdesc
-% methoddesc - methods, like funcdesc but has an optional parameter
-% to give the type name: \begin{methoddesc}[mytype]{name}{args}
-% By default, the type name will be the name of the
-% last class defined using classdesc. The type name
-% is required if the type is implemented in C (because
-% there's no classdesc) or if the class isn't directly
-% documented (if it's private).
-% memberdesc - data members, like datadesc, but with an optional
-% type name like methoddesc.
-
-
-% ==== 4. ====
-% Now is probably a good time for a complete example. (Alternatively,
-% an example giving the flavor of the module may be given before the
-% detailed list of functions.)
-
-\subsection{Example \label{spam-example}}
-
-The following example demonstrates how to open a can of spam using the
-\module{spam} module.
-
-\begin{verbatim}
->>> import spam
->>> can = spam.open('/etc/passwd')
->>> can.empty()
->>> can.close()
-\end{verbatim}
-% Note that there is no trailing ">>> " prompt shown.
-
-% ==== 5. ====
-% If your module defines new object types (for a built-in module) or
-% classes (for a module written in Python), you should list the
-% methods and instance variables (if any) of each type or class in a
-% separate subsection.
-
-\subsection{Spam Objects}
-\label{spam-objects}
-% This label is generally useful for referencing this section, but is
-% also used to give a filename when generating HTML.
-
-Spam objects, as returned by \function{open()} above, have the
-following methods:
-
-\begin{methoddesc}[spam]{empty}{}
-Empty the can into the trash.
-\end{methoddesc}
+++ /dev/null
-\author{Guido van Rossum\\
- Fred L. Drake, Jr., editor}
-\authoraddress{
- \strong{PythonLabs}\\
- E-mail: \email{python-docs@python.org}
-}
-
-\date{April 15, 2001} % XXX update before release!
-\release{2.1} % software release, not documentation
-\setshortversion{2.1} % major.minor only for software
+++ /dev/null
-\begin{small}
-Copyright \copyright{} 2001 Python Software Foundation.
-All rights reserved.
-
-Copyright \copyright{} 2000 BeOpen.com.
-All rights reserved.
-
-Copyright \copyright{} 1995-2000 Corporation for National Research Initiatives.
-All rights reserved.
-
-Copyright \copyright{} 1991-1995 Stichting Mathematisch Centrum.
-All rights reserved.
-
-%%begin{latexonly}
-\vskip 4mm
-%%end{latexonly}
-
-\centerline{\strong{BEOPEN.COM TERMS AND CONDITIONS FOR PYTHON 2.0}}
-
-\centerline{\strong{BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1}}
-
-\begin{enumerate}
-
-\item
-This LICENSE AGREEMENT is between BeOpen.com (``BeOpen''), having an
-office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
-Individual or Organization (``Licensee'') accessing and otherwise
-using this software in source or binary form and its associated
-documentation (``the Software'').
-
-\item
-Subject to the terms and conditions of this BeOpen Python License
-Agreement, BeOpen hereby grants Licensee a non-exclusive,
-royalty-free, world-wide license to reproduce, analyze, test, perform
-and/or display publicly, prepare derivative works, distribute, and
-otherwise use the Software alone or in any derivative version,
-provided, however, that the BeOpen Python License is retained in the
-Software, alone or in any derivative version prepared by Licensee.
-
-\item
-BeOpen is making the Software available to Licensee on an ``AS IS''
-basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-\item
-BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
-SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
-AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
-DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-\item
-This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-\item
-This License Agreement shall be governed by and interpreted in all
-respects by the law of the State of California, excluding conflict of
-law provisions. Nothing in this License Agreement shall be deemed to
-create any relationship of agency, partnership, or joint venture
-between BeOpen and Licensee. This License Agreement does not grant
-permission to use BeOpen trademarks or trade names in a trademark
-sense to endorse or promote products or services of Licensee, or any
-third party. As an exception, the ``BeOpen Python'' logos available
-at http://www.pythonlabs.com/logos.html may be used according to the
-permissions granted on that web page.
-
-\item
-By copying, installing or otherwise using the software, Licensee
-agrees to be bound by the terms and conditions of this License
-Agreement.
-\end{enumerate}
-
-
-\centerline{\strong{CNRI OPEN SOURCE GPL-COMPATIBLE LICENSE AGREEMENT}}
-
-Python 1.6.1 is made available subject to the terms and conditions in
-CNRI's License Agreement. This Agreement together with Python 1.6.1 may
-be located on the Internet using the following unique, persistent
-identifier (known as a handle): 1895.22/1013. This Agreement may also
-be obtained from a proxy server on the Internet using the following
-URL: \url{http://hdl.handle.net/1895.22/1013}.
-
-
-\centerline{\strong{CWI PERMISSIONS STATEMENT AND DISCLAIMER}}
-
-Copyright \copyright{} 1991 - 1995, Stichting Mathematisch Centrum
-Amsterdam, The Netherlands. All rights reserved.
-
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose and without fee is hereby granted,
-provided that the above copyright notice appear in all copies and that
-both that copyright notice and this permission notice appear in
-supporting documentation, and that the name of Stichting Mathematisch
-Centrum or CWI not be used in advertising or publicity pertaining to
-distribution of the software without specific, written prior
-permission.
-
-STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
-THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
-FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
-FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-\end{small}
+++ /dev/null
-%
-% LaTeX commands and macros needed for the two Distutils manuals,
-% inst.tex and dist.tex.
-%
-% $Id: distutils.sty,v 1.1.1.1 2001/07/16 11:53:03 msjogren Exp $
-%
-
-% My gripe list about the Python style files:
-% * I want italics in verbatim environments for variable
-% text (verbatim.sty?)
-% * I hate escaping underscores (url.sty fixes this)
-
-% '\command' is for Distutils commands which, depending on your
-% perspective, are just arguments to the setup script, or sub-
-% commands of the setup script, or the classes that implement
-% each "command".
-\newcommand{\command}[1]{\code{#1}}
-
-% '\option' is for Distutils options *in* the setup script. Command-
-% line options *to* the setup script are marked up in the usual
-% way, ie. with '\programopt' or '\longprogramopt'
-\newcommand{\option}[1]{\textsf{\small{#1}}}
-
-% '\filevar' is for variable components of file/path names -- eg.
-% when you put 'prefix' in a pathname, you mark it up with
-% '\filevar' so that it still looks pathname-ish, but is
-% distinguished from the literal part of the path. Fred says
-% this can be accomplished just fine with '\var', but I violently
-% disagree. Pistols at dawn will sort this one out.
-\newcommand{\filevar}[1]{{\textsl{\filenq{#1}}}}
-
-% Just while the code and docs are still under development.
-\newcommand{\XXX}[1]{\textbf{**#1**}}
+++ /dev/null
-%%% Derived from the original fncychap.sty,
-%%% but changed ``TWELV'' to ``TWELVE''.
-
-%%% Copyright Ulf A. Lindgren
-%%% Department of Applied Electronics
-%%% Chalmers University of Technology
-%%% S-412 96 Gothenburg, Sweden
-%%% E-mail lindgren@ae.chalmers.se
-%%%
-%%% Note Permission is granted to modify this file under
-%%% the condition that it is saved using another
-%%% file and package name.
-%%%
-%%% Revision 1.1
-%%%
-%%% Jan. 8th Modified package name base date option
-%%% Jan. 22th Modified FmN and FmTi for error in book.cls
-%%% \MakeUppercase{#}->{\MakeUppercase#}
-%%% Apr. 6th Modified Lenny option to prevent undesired
-%%% skip of line.
-%%% Nov. 8th Fixed \@chapapp for AMS
-%%% Feb. 11th Fixed appendix problem related to Bjarne
-%%% Last modified Feb. 11th 1998
-
-\NeedsTeXFormat{LaTeX2e}[1995/12/01]
-\ProvidesPackage{fncychap}
- [1997/04/06 v1.11
- LaTeX package (Revised chapters)]
-
-%%%% DEFINITION OF Chapapp variables
-\newcommand{\CNV}{\huge\bfseries}
-\newcommand{\ChNameVar}[1]{\renewcommand{\CNV}{#1}}
-
-
-%%%% DEFINITION OF TheChapter variables
-\newcommand{\CNoV}{\huge\bfseries}
-\newcommand{\ChNumVar}[1]{\renewcommand{\CNoV}{#1}}
-
-\newif\ifUCN
-\UCNfalse
-\newif\ifLCN
-\LCNfalse
-\def\ChNameLowerCase{\LCNtrue\UCNfalse}
-\def\ChNameUpperCase{\UCNtrue\LCNfalse}
-\def\ChNameAsIs{\UCNfalse\LCNfalse}
-
-%%%%% Fix for AMSBook 971008
-
-\@ifundefined{@chapapp}{\let\@chapapp\chaptername}{}
-
-
-%%%%% Fix for Bjarne and appendix 980211
-
-\newif\ifinapp
-\inappfalse
-\renewcommand\appendix{\par
- \setcounter{chapter}{0}%
- \setcounter{section}{0}%
- \inapptrue%
- \renewcommand\@chapapp{\appendixname}%
- \renewcommand\thechapter{\@Alph\c@chapter}}
-
-%%%%%
-
-\newcommand{\FmN}[1]{%
-\ifUCN
- {\MakeUppercase#1}\LCNfalse
-\else
- \ifLCN
- {\MakeLowercase#1}\UCNfalse
- \else #1
- \fi
-\fi}
-
-
-%%%% DEFINITION OF Title variables
-\newcommand{\CTV}{\Huge\bfseries}
-\newcommand{\ChTitleVar}[1]{\renewcommand{\CTV}{#1}}
-
-%%%% DEFINITION OF the basic rule width
-\newlength{\RW}
-\setlength{\RW}{1pt}
-\newcommand{\ChRuleWidth}[1]{\setlength{\RW}{#1}}
-
-\newif\ifUCT
-\UCTfalse
-\newif\ifLCT
-\LCTfalse
-\def\ChTitleLowerCase{\LCTtrue\UCTfalse}
-\def\ChTitleUpperCase{\UCTtrue\LCTfalse}
-\def\ChTitleAsIs{\UCTfalse\LCTfalse}
-\newcommand{\FmTi}[1]{%
-\ifUCT
-
- {\MakeUppercase#1}\LCTfalse
-\else
- \ifLCT
- {\MakeLowercase#1}\UCTfalse
- \else #1
- \fi
-\fi}
-
-
-
-\newlength{\mylen}
-\newlength{\myhi}
-\newlength{\px}
-\newlength{\py}
-\newlength{\pyy}
-\newlength{\pxx}
-
-
-\def\mghrulefill#1{\leavevmode\leaders\hrule\@height #1\hfill\kern\z@}
-
-\newcommand{\DOCH}{%
- \CNV\FmN{\@chapapp}\space \CNoV\thechapter
- \par\nobreak
- \vskip 20\p@
- }
-\newcommand{\DOTI}[1]{%
- \CTV\FmTi{#1}\par\nobreak
- \vskip 40\p@
- }
-\newcommand{\DOTIS}[1]{%
- \CTV\FmTi{#1}\par\nobreak
- \vskip 40\p@
- }
-
-%%%%%% SONNY DEF
-
-\DeclareOption{Sonny}{%
- \ChNameVar{\Large\sf}
- \ChNumVar{\Huge}
- \ChTitleVar{\Large\sf}
- \ChRuleWidth{0.5pt}
- \ChNameUpperCase
- \renewcommand{\DOCH}{%
- \raggedleft
- \CNV\FmN{\@chapapp}\space \CNoV\thechapter
- \par\nobreak
- \vskip 40\p@}
- \renewcommand{\DOTI}[1]{%
- \CTV\raggedleft\mghrulefill{\RW}\par\nobreak
- \vskip 5\p@
- \CTV\FmTi{#1}\par\nobreak
- \mghrulefill{\RW}\par\nobreak
- \vskip 40\p@}
- \renewcommand{\DOTIS}[1]{%
- \CTV\raggedleft\mghrulefill{\RW}\par\nobreak
- \vskip 5\p@
- \CTV\FmTi{#1}\par\nobreak
- \mghrulefill{\RW}\par\nobreak
- \vskip 40\p@}
-}
-
-%%%%%% LENNY DEF
-
-\DeclareOption{Lenny}{%
-
- \ChNameVar{\fontsize{14}{16}\usefont{OT1}{phv}{m}{n}\selectfont}
- \ChNumVar{\fontsize{60}{62}\usefont{OT1}{ptm}{m}{n}\selectfont}
- \ChTitleVar{\Huge\bfseries\rm}
- \ChRuleWidth{1pt}
- \renewcommand{\DOCH}{%
- \settowidth{\px}{\CNV\FmN{\@chapapp}}
- \addtolength{\px}{2pt}
- \settoheight{\py}{\CNV\FmN{\@chapapp}}
- \addtolength{\py}{1pt}
-
- \settowidth{\mylen}{\CNV\FmN{\@chapapp}\space\CNoV\thechapter}
- \addtolength{\mylen}{1pt}
- \settowidth{\pxx}{\CNoV\thechapter}
- \addtolength{\pxx}{-1pt}
-
- \settoheight{\pyy}{\CNoV\thechapter}
- \addtolength{\pyy}{-2pt}
- \setlength{\myhi}{\pyy}
- \addtolength{\myhi}{-1\py}
- \par
- \parbox[b]{\textwidth}{%
- \rule[\py]{\RW}{\myhi}%
- \hskip -\RW%
- \rule[\pyy]{\px}{\RW}%
- \hskip -\px%
- \raggedright%
- \CNV\FmN{\@chapapp}\space\CNoV\thechapter%
- \hskip1pt%
- \mghrulefill{\RW}%
- \rule{\RW}{\pyy}\par\nobreak%
- \vskip -\baselineskip%
- \vskip -\pyy%
- \hskip \mylen%
- \mghrulefill{\RW}\par\nobreak%
- \vskip \pyy}%
- \vskip 20\p@}
-
-
- \renewcommand{\DOTI}[1]{%
- \raggedright
- \CTV\FmTi{#1}\par\nobreak
- \vskip 40\p@}
-
- \renewcommand{\DOTIS}[1]{%
- \raggedright
- \CTV\FmTi{#1}\par\nobreak
- \vskip 40\p@}
- }
-
-
-%%%%%%% GLENN DEF
-
-
-\DeclareOption{Glenn}{%
- \ChNameVar{\bfseries\Large\sf}
- \ChNumVar{\Huge}
- \ChTitleVar{\bfseries\Large\rm}
- \ChRuleWidth{1pt}
- \ChNameUpperCase
- \ChTitleUpperCase
- \renewcommand{\DOCH}{%
- \settoheight{\myhi}{\CTV\FmTi{Test}}
- \setlength{\py}{\baselineskip}
- \addtolength{\py}{\RW}
- \addtolength{\py}{\myhi}
- \setlength{\pyy}{\py}
- \addtolength{\pyy}{-1\RW}
-
- \raggedright
- \CNV\FmN{\@chapapp}\space\CNoV\thechapter
- \hskip 3pt\mghrulefill{\RW}\rule[-1\pyy]{2\RW}{\py}\par\nobreak}
-
- \renewcommand{\DOTI}[1]{%
- \addtolength{\pyy}{-4pt}
- \settoheight{\myhi}{\CTV\FmTi{#1}}
- \addtolength{\myhi}{\py}
- \addtolength{\myhi}{-1\RW}
- \vskip -1\pyy
- \rule{2\RW}{\myhi}\mghrulefill{\RW}\hskip 2pt
- \raggedleft\CTV\FmTi{#1}\par\nobreak
- \vskip 80\p@}
-
- \renewcommand{\DOTIS}[1]{%
- \setlength{\py}{10pt}
- \setlength{\pyy}{\py}
- \addtolength{\pyy}{\RW}
- \setlength{\myhi}{\baselineskip}
- \addtolength{\myhi}{\pyy}
- \mghrulefill{\RW}\rule[-1\py]{2\RW}{\pyy}\par\nobreak
-% \addtolength{}{}
-\vskip -1\baselineskip
- \rule{2\RW}{\myhi}\mghrulefill{\RW}\hskip 2pt
- \raggedleft\CTV\FmTi{#1}\par\nobreak
- \vskip 60\p@}
- }
-
-%%%%%%% CONNY DEF
-
-\DeclareOption{Conny}{%
- \ChNameUpperCase
- \ChTitleUpperCase
- \ChNameVar{\centering\Huge\rm\bfseries}
- \ChNumVar{\Huge}
- \ChTitleVar{\centering\Huge\rm}
- \ChRuleWidth{2pt}
-
- \renewcommand{\DOCH}{%
- \mghrulefill{3\RW}\par\nobreak
- \vskip -0.5\baselineskip
- \mghrulefill{\RW}\par\nobreak
- \CNV\FmN{\@chapapp}\space \CNoV\thechapter
- \par\nobreak
- \vskip -0.5\baselineskip
- }
- \renewcommand{\DOTI}[1]{%
- \mghrulefill{\RW}\par\nobreak
- \CTV\FmTi{#1}\par\nobreak
- \vskip 60\p@
- }
- \renewcommand{\DOTIS}[1]{%
- \mghrulefill{\RW}\par\nobreak
- \CTV\FmTi{#1}\par\nobreak
- \vskip 60\p@
- }
- }
-
-%%%%%%% REJNE DEF
-
-\DeclareOption{Rejne}{%
-
- \ChNameUpperCase
- \ChTitleUpperCase
- \ChNameVar{\centering\Large\rm}
- \ChNumVar{\Huge}
- \ChTitleVar{\centering\Huge\rm}
- \ChRuleWidth{1pt}
- \renewcommand{\DOCH}{%
- \settoheight{\py}{\CNoV\thechapter}
- \addtolength{\py}{-1pt}
- \CNV\FmN{\@chapapp}\par\nobreak
- \vskip 20\p@
- \setlength{\myhi}{2\baselineskip}
- \setlength{\px}{\myhi}
- \addtolength{\px}{-1\RW}
- \rule[-1\px]{\RW}{\myhi}\mghrulefill{\RW}\hskip
- 10pt\raisebox{-0.5\py}{\CNoV\thechapter}\hskip
-10pt\mghrulefill{\RW}\rule[-1\px]{\RW}{\myhi}\par\nobreak
- \vskip -1\p@
- }
- \renewcommand{\DOTI}[1]{%
- \setlength{\mylen}{\textwidth}
- \addtolength{\mylen}{-2\RW}
- {\vrule width\RW}\parbox{\mylen}{\CTV\FmTi{#1}}{\vrule
-width\RW}\par\nobreak
- \vskip
--1pt\rule{\RW}{2\baselineskip}\mghrulefill{\RW}\rule{\RW}{2\baselineskip}
- \vskip 60\p@
- }
- \renewcommand{\DOTIS}[1]{%
- \setlength{\py}{\fboxrule}
- \setlength{\fboxrule}{\RW}
- \setlength{\mylen}{\textwidth}
- \addtolength{\mylen}{-2\RW}
- \fbox{\parbox{\mylen}{\vskip
-2\baselineskip\CTV\FmTi{#1}\par\nobreak\vskip \baselineskip}}
- \setlength{\fboxrule}{\py}
- \vskip 60\p@
- }
- }
-
-
-%%%%%%% BJARNE DEF
-
-\DeclareOption{Bjarne}{%
- \ChNameUpperCase
- \ChTitleUpperCase
- \ChNameVar{\raggedleft\normalsize\rm}
- \ChNumVar{\raggedleft \bfseries\Large}
- \ChTitleVar{\raggedleft \Large\rm}
- \ChRuleWidth{1pt}
-
-
-%% Note thechapter -> c@chapter fix appendix bug
-
- \newcounter{AlphaCnt}
- \newcounter{AlphaDecCnt}
- \newcommand{\AlphaNo}{%
- \ifcase\number\theAlphaCnt
- \ifnum\c@chapter=0
- ZERO\else{}\fi
- \or ONE\or TWO\or THREE\or FOUR\or FIVE
- \or SIX\or SEVEN\or EIGHT\or NINE\or TEN
- \or ELEVEN\or TWELVE\or THIRTEEN\or FOURTEEN\or FIFTEEN
- \or SIXTEEN\or SEVENTEEN\or EIGHTEEN\or NINETEEN\fi
-}
-
- \newcommand{\AlphaDecNo}{%
- \setcounter{AlphaDecCnt}{0}
- \@whilenum\number\theAlphaCnt>0\do
- {\addtocounter{AlphaCnt}{-10}
- \addtocounter{AlphaDecCnt}{1}}
- \ifnum\number\theAlphaCnt=0
- \else
- \addtocounter{AlphaDecCnt}{-1}
- \addtocounter{AlphaCnt}{10}
- \fi
-
-
- \ifcase\number\theAlphaDecCnt\or TEN\or TWENTY\or THIRTY\or
- FORTY\or FIFTY\or SIXTY\or SEVENTY\or EIGHTY\or NINETY\fi
- }
- \newcommand{\TheAlphaChapter}{%
-
- \ifinapp
- \thechapter
- \else
- \setcounter{AlphaCnt}{\c@chapter}
- \ifnum\c@chapter<20
- \AlphaNo
- \else
- \AlphaDecNo\AlphaNo
- \fi
- \fi
- }
- \renewcommand{\DOCH}{%
- \mghrulefill{\RW}\par\nobreak
- \CNV\FmN{\@chapapp}\par\nobreak
- \CNoV\TheAlphaChapter\par\nobreak
- \vskip -1\baselineskip\vskip 5pt\mghrulefill{\RW}\par\nobreak
- \vskip 20\p@
- }
- \renewcommand{\DOTI}[1]{%
- \CTV\FmTi{#1}\par\nobreak
- \vskip 40\p@
- }
- \renewcommand{\DOTIS}[1]{%
- \CTV\FmTi{#1}\par\nobreak
- \vskip 40\p@
- }
-}
-
-\DeclareOption*{%
- \PackageWarning{fancychapter}{unknown style option}
- }
-
-\ProcessOptions* \relax
-
-\def\@makechapterhead#1{%
- \vspace*{50\p@}%
- {\parindent \z@ \raggedright \normalfont
- \ifnum \c@secnumdepth >\m@ne
- \DOCH
- \fi
- \interlinepenalty\@M
- \DOTI{#1}
- }}
-\def\@schapter#1{\if@twocolumn
- \@topnewpage[\@makeschapterhead{#1}]%
- \else
- \@makeschapterhead{#1}%
- \@afterheading
- \fi}
-\def\@makeschapterhead#1{%
- \vspace*{50\p@}%
- {\parindent \z@ \raggedright
- \normalfont
- \interlinepenalty\@M
- \DOTIS{#1}
- \vskip 40\p@
- }}
-
-\endinput
-
-
+++ /dev/null
-%
-% howto.cls for the Python documentation
-%
-
-\NeedsTeXFormat{LaTeX2e}[1995/12/01]
-\ProvidesClass{howto}
- [1998/02/25 Document class (Python HOWTO)]
-
-\RequirePackage{pypaper}
-
-% Change the options here to get a different set of basic options, This
-% is where to add things like "a4paper" or "10pt".
-%
-\LoadClass[twoside]{article}
-
-\setcounter{secnumdepth}{1}
-
-% Optional packages:
-%
-% If processing of these documents fails at your TeX installation,
-% these may be commented out (independently) to make things work.
-% These are both supplied with the current version of the teTeX
-% distribution.
-%
-% The "fancyhdr" package makes nicer page footers reasonable to
-% implement, and is used to put the chapter and section information in
-% the footers.
-%
-\RequirePackage{fancyhdr}\typeout{Using fancier footers than usual.}
-
-
-% Required package:
-%
-% This gives us all the Python-specific markup that we really want.
-% This should come last. Do not change this.
-%
-\RequirePackage{python}
-
-% support for module synopsis sections:
-\newcommand{\py@ModSynopsisFilename}{\jobname.syn}
-
-
-% need to do one of these....
-\newcommand{\py@doHorizontalRule}{\rule{\textwidth}{1pt}}
-
-
-% Change the title page to look a bit better, and fit in with the
-% fncychap ``Bjarne'' style a bit better.
-%
-\renewcommand{\maketitle}{
- \py@doHorizontalRule
- \@ifundefined{pdfinfo}{}{{
- % This \def is required to deal with multi-line authors; it
- % changes \\ to ', ' (comma-space), making it pass muster for
- % generating document info in the PDF file.
- \def\\{, }
- \pdfinfo{
- /Author (\@author)
- /Title (\@title)
- }
- }}
- \begin{flushright}
- {\rm\Huge\py@HeaderFamily \@title} \par
- {\em\large\py@HeaderFamily \py@release} \par
- \vspace{25pt}
- {\Large\py@HeaderFamily \@author} \par
- \vspace{25pt}
- \@date \par
- \py@authoraddress \par
- \end{flushright}
- \@thanks
- \setcounter{footnote}{0}
- \let\thanks\relax\let\maketitle\relax
- \gdef\@thanks{}\gdef\@author{}\gdef\@title{}
-}
-
-
-\let\py@OldTableofcontents=\tableofcontents
-\renewcommand{\tableofcontents}{
- \begingroup
- \parskip = 0mm
- \py@OldTableofcontents
- \endgroup
- \py@doHorizontalRule
- \vspace{12pt}
- \py@doing@page@targetstrue
-}
-
-% Fix the theindex environment to add an entry to the Table of
-% Contents; this is much nicer than just having to jump to the end of
-% the book and flip around, especially with multiple indexes.
-%
-\let\py@OldTheindex=\theindex
-\renewcommand{\theindex}{
- \clearpage
- \py@OldTheindex
- \addcontentsline{toc}{section}{\indexname}
-}
-
-\@ifundefined{fancyhf}{
- \pagestyle{plain}}{
- \pagestyle{normal}} % start this way; change for
-\pagenumbering{arabic} % ToC & chapters
-\setcounter{secnumdepth}{2}
-
-\thispagestyle{empty}
+++ /dev/null
-% Created by Fred L. Drake, Jr. <fdrake@acm.org>, as part of the
-% Python Documentation Project.
-%
-% Define some simple markup for the LaTeX command documentation:
-
-\ProvidesPackage{ltxmarkup}
-\RequirePackage{python} % fulllineitems environment
-
-% These two macros are used in constructing the last parameter to the
-% envdesc and macrodesc environments.
-
-\newcommand{\py@ltx@optparam}[1]{{[}\var{#1}{]}}
-\newcommand{\py@ltx@param}[1]{\{\var{#1}\}}
-
-\newenvironment{envdesc}[2]{
- \begin{fulllineitems}
- \item[\code{\e begin\{{\bfseries #1}\}{%
- \let\op=\py@ltx@optparam%
- \let\p=\py@ltx@param%
- \let\unspecified=\py@unspecified%
- \let\moreargs=\py@moreargs%
- #2}}]
- \item[\code{\e end\{{\bfseries #1}\}}]
- \index{#1 environment@\idxcode{#1} environment}
- \index{environments!#1@\idxcode{#1}}
-}{\end{fulllineitems}}
-
-\newenvironment{macrodesc}[2]{
- \begin{fulllineitems}
- \item[\code{{\e\bfseries#1}{%
- \let\op=\py@ltx@optparam%
- \let\p=\py@ltx@param%
- \let\unspecified=\py@unspecified%
- \let\moreargs=\py@moreargs%
- #2}}]
- \index{#1@\idxcode{\e #1}}
-}{\end{fulllineitems}}
-
-\newcommand{\env}[1]{\code{#1}}
-\newcommand{\macro}[1]{\code{\e#1}}
+++ /dev/null
-%
-% manual.cls for the Python documentation
-%
-
-\NeedsTeXFormat{LaTeX2e}[1995/12/01]
-\ProvidesClass{manual}
- [1998/03/03 Document class (Python manual)]
-
-\RequirePackage{pypaper}
-
-% Change the options here to get a different set of basic options, but only
-% if you have to. Paper and font size should be adjusted in pypaper.sty.
-%
-\LoadClass[\py@paper,\py@ptsize,twoside,openright]{report}
-
-\setcounter{secnumdepth}{2}
-
-% Optional packages:
-%
-% If processing of these documents fails at your TeX installation,
-% these may be commented out (independently) to make things work.
-% These are both supplied with the current version of the teTeX
-% distribution.
-%
-% The "fancyhdr" package makes nicer page footers reasonable to
-% implement, and is used to put the chapter and section information in
-% the footers.
-%
-\RequirePackage{fancyhdr}\typeout{Using fancier footers than usual.}
-
-
-% Required packages:
-%
-% The "fncychap" package is used to get the nice chapter headers. The
-% .sty file is distributed with Python, so you should not need to disable
-% it. You'd also end up with a mixed page style; uglier than stock LaTeX!
-%
-\RequirePackage[Bjarne]{fncychap}\typeout{Using fancy chapter headings.}
-% Do horizontal rules it this way to match:
-\newcommand{\py@doHorizontalRule}{\mghrulefill{\RW}}
-%
-%
-% This gives us all the Python-specific markup that we really want.
-% This should come last. Do not change this.
-%
-\RequirePackage{python}
-
-% support for module synopsis sections:
-\newcommand{\py@ModSynopsisFilename}{\jobname\thechapter.syn}
-\let\py@OldChapter=\chapter
-\renewcommand{\chapter}{
- \py@ProcessModSynopsis
- \py@closeModSynopsisFile
- \py@OldChapter
-}
-
-
-% Change the title page to look a bit better, and fit in with the
-% fncychap ``Bjarne'' style a bit better.
-%
-\renewcommand{\maketitle}{%
- \begin{titlepage}%
- \let\footnotesize\small
- \let\footnoterule\relax
- \py@doHorizontalRule%
- \@ifundefined{pdfinfo}{}{{
- % This \def is required to deal with multi-line authors; it
- % changes \\ to ', ' (comma-space), making it pass muster for
- % generating document info in the PDF file.
- \def\\{, }
- \pdfinfo{
- /Author (\@author)
- /Title (\@title)
- }
- }}
- \begin{flushright}%
- {\rm\Huge\py@HeaderFamily \@title \par}%
- {\em\LARGE\py@HeaderFamily \py@release \par}
- \vfill
- {\LARGE\py@HeaderFamily \@author \par}
- \vfill\vfill
- {\large
- \@date \par
- \vfill
- \py@authoraddress \par
- }%
- \end{flushright}%\par
- \@thanks
- \end{titlepage}%
- \setcounter{footnote}{0}%
- \let\thanks\relax\let\maketitle\relax
- \gdef\@thanks{}\gdef\@author{}\gdef\@title{}
-}
-
-
-% Catch the end of the {abstract} environment, but here make sure the
-% abstract is followed by a blank page if the 'openright' option is used.
-%
-\let\py@OldEndAbstract=\endabstract
-\renewcommand{\endabstract}{
- \if@openright
- \ifodd\value{page}
- \typeout{Adding blank page after the abstract.}
- \vfil\pagebreak
- \fi
- \fi
- \py@OldEndAbstract
-}
-
-% This wraps the \tableofcontents macro with all the magic to get the
-% spacing right and have the right number of pages if the 'openright'
-% option has been used. This eliminates a fair amount of crud in the
-% individual document files.
-%
-\let\py@OldTableofcontents=\tableofcontents
-\renewcommand{\tableofcontents}{%
- \setcounter{page}{1}%
- \pagebreak%
- \pagestyle{plain}%
- {%
- \parskip = 0mm%
- \py@OldTableofcontents%
- \if@openright%
- \ifodd\value{page}%
- \typeout{Adding blank page after the table of contents.}%
- \pagebreak\hspace{0pt}%
- \fi%
- \fi%
- \cleardoublepage%
- }%
- \pagenumbering{arabic}%
- \@ifundefined{fancyhf}{}{\pagestyle{normal}}%
- \py@doing@page@targetstrue%
-}
-% This is needed to get the width of the section # area wide enough in the
-% library reference. Doing it here keeps it the same for all the manuals.
-%
-\renewcommand*\l@section{\@dottedtocline{1}{1.5em}{2.6em}}
-\renewcommand*\l@subsection{\@dottedtocline{2}{4.1em}{3.5em}}
-\setcounter{tocdepth}{1}
-
-
-% Fix the theindex environment to add an entry to the Table of
-% Contents; this is much nicer than just having to jump to the end of
-% the book and flip around, especially with multiple indexes.
-%
-\let\py@OldTheindex=\theindex
-\renewcommand{\theindex}{
- \cleardoublepage
- \py@OldTheindex
- \addcontentsline{toc}{chapter}{\indexname}
-}
+++ /dev/null
-%
-% Change this to say a4paper instead of letterpaper if you want A4. These
-% are the latex defaults.
-%
-\newcommand{\py@paper}{letterpaper}
-\newcommand{\py@ptsize}{10pt}
-
-% These set up the fonts for the documents.
-%
-% The "times" package makes the default font the PostScript Times
-% font, which makes for smaller PostScript and a font that more people
-% like.
-%
-% The "avant" package causes the AvantGarde font to be used for
-% sans-serif text, instead of the uglier Helvetica set up by the "times"
-% package.
-%
-\RequirePackage{times}\typeout{Using Times instead of Computer Modern.}
+++ /dev/null
-line_max 100
-headings_flag 1
-heading_prefix " \\bigletter "
-
-preamble "\\begin{theindex}
-\\def\\bigletter#1{{\\Large\\sffamily#1}\\nopagebreak\\vspace{1mm}}
-
-"
-
-symhead_positive "{Symbols}"
-numhead_positive "{Numbers}"
+++ /dev/null
-%
-% python.sty for the Python docummentation [works only with with Latex2e]
-%
-
-\NeedsTeXFormat{LaTeX2e}[1995/12/01]
-\ProvidesPackage{python}
- [1998/01/11 LaTeX package (Python markup)]
-
-\RequirePackage{longtable}
-
-% Uncomment these two lines to ignore the paper size and make the page
-% size more like a typical published manual.
-%\renewcommand{\paperheight}{9in}
-%\renewcommand{\paperwidth}{8.5in} % typical squarish manual
-%\renewcommand{\paperwidth}{7in} % O'Reilly ``Programmming Python''
-
-% These packages can be used to add marginal annotations which indicate
-% index entries and labels; useful for reviewing this messy documentation!
-%
-%\RequirePackage{showkeys}
-%\RequirePackage{showidx}
-
-% for PDF output, use maximal compression & a lot of other stuff
-% (test for PDF recommended by Tanmoy Bhattacharya <tanmoy@qcd.lanl.gov>)
-%
-\newif\ifpy@doing@page@targets
-\py@doing@page@targetsfalse
-
-\ifx\pdfoutput\undefined\else\ifcase\pdfoutput
-\else
- \input{pdfcolor}
- \let\py@LinkColor=\NavyBlue
- \let\py@NormalColor=\Black
- \pdfcompresslevel=9
- \pdfpagewidth=\paperwidth % page width of PDF output
- \pdfpageheight=\paperheight % page height of PDF output
- %
- % Pad the number with '0' to 3 digits wide so no page name is a prefix
- % of any other.
- %
- \newcommand{\py@targetno}[1]{\ifnum#1<100 0\fi\ifnum#1<10 0\fi#1}
- \newcommand{\py@pageno}{\py@targetno\thepage}
- %
- % This definition allows the entries in the page-view of the ToC to be
- % active links. Some work, some don't.
- %
- \let\py@OldContentsline=\contentsline
- %
- % Macro that takes two args: the name to link to and the content of
- % the link. This takes care of the PDF magic, getting the colors
- % the same for each link, and avoids having lots of garbage all over
- % this style file.
- \newcommand{\py@linkToName}[2]{%
- \pdfannotlink attr{/Border [0 0 0]} goto name{#1}%
- \py@LinkColor#2\py@NormalColor%
- \pdfendlink%
- }
- % Compute the padded page number separately since we end up with a pair of
- % \relax tokens; this gets the right string computed and works.
- \renewcommand{\contentsline}[3]{%
- \def\my@pageno{\py@targetno{#3}}%
- \py@OldContentsline{#1}{\py@linkToName{page\my@pageno}{#2}}{#3}%
- }
- \AtEndDocument{
- \InputIfFileExists{\jobname.bkm}{\pdfcatalog{/PageMode /UseOutlines}}{}
- }
- \newcommand{\py@target}[1]{%
- \ifpy@doing@page@targets%
- {\pdfdest name{#1} xyz}%
- \fi%
- }
- \let\py@OldLabel=\label
- \renewcommand{\label}[1]{%
- \py@OldLabel{#1}%
- \py@target{label-#1}%
- }
- % This stuff adds a page# destination to every PDF page, where # is three
- % digits wide, padded with leading zeros. This doesn't really help with
- % the frontmatter, but does fine with the body.
- %
- % This is *heavily* based on the hyperref package.
- %
- \def\@begindvi{%
- \unvbox \@begindvibox
- \@hyperfixhead
- }
- \def\@hyperfixhead{%
- \let\H@old@thehead\@thehead
- \global\def\@foo{\py@target{page\py@pageno}}%
- \expandafter\ifx\expandafter\@empty\H@old@thehead
- \def\H@old@thehead{\hfil}\fi
- \def\@thehead{\@foo\relax\H@old@thehead}%
- }
-\fi\fi
-
-% Increase printable page size (copied from fullpage.sty)
-\topmargin 0pt
-\advance \topmargin by -\headheight
-\advance \topmargin by -\headsep
-
-% attempt to work a little better for A4 users
-\textheight \paperheight
-\advance\textheight by -2in
-
-\oddsidemargin 0pt
-\evensidemargin 0pt
-%\evensidemargin -.25in % for ``manual size'' documents
-\marginparwidth 0.5in
-
-\textwidth \paperwidth
-\advance\textwidth by -2in
-
-
-% Style parameters and macros used by most documents here
-\raggedbottom
-\sloppy
-\parindent = 0mm
-\parskip = 2mm
-\hbadness = 5000 % don't print trivial gripes
-
-\pagestyle{empty} % start this way; change for
-\pagenumbering{roman} % ToC & chapters
-
-% Use this to set the font family for headers and other decor:
-\newcommand{\py@HeaderFamily}{\sffamily}
-
-% Redefine the 'normal' header/footer style when using "fancyhdr" package:
-\@ifundefined{fancyhf}{}{
- % Use \pagestyle{normal} as the primary pagestyle for text.
- \fancypagestyle{normal}{
- \fancyhf{}
- \fancyfoot[LE,RO]{{\py@HeaderFamily\thepage}}
- \fancyfoot[LO]{{\py@HeaderFamily\nouppercase{\rightmark}}}
- \fancyfoot[RE]{{\py@HeaderFamily\nouppercase{\leftmark}}}
- \renewcommand{\headrulewidth}{0pt}
- \renewcommand{\footrulewidth}{0.4pt}
- }
- % Update the plain style so we get the page number & footer line,
- % but not a chapter or section title. This is to keep the first
- % page of a chapter and the blank page between chapters `clean.'
- \fancypagestyle{plain}{
- \fancyhf{}
- \fancyfoot[LE,RO]{{\py@HeaderFamily\thepage}}
- \renewcommand{\headrulewidth}{0pt}
- \renewcommand{\footrulewidth}{0.4pt}
- }
- % Redefine \cleardoublepage so that the blank page between chapters
- % gets the plain style and not the fancy style. This is described
- % in the documentation for the fancyhdr package by Piet von Oostrum.
- \@ifundefined{chapter}{}{
- \renewcommand{\cleardoublepage}{
- \clearpage\if@openright \ifodd\c@page\else
- \hbox{}
- \thispagestyle{plain}
- \newpage
- \if@twocolumn\hbox{}\newpage\fi\fi\fi
- }
- }
-}
-
-% This sets up the {verbatim} environment to be indented and a minipage,
-% and to have all the other mostly nice properties that we want for
-% code samples.
-
-\let\py@OldVerbatim=\verbatim
-\let\py@OldEndVerbatim=\endverbatim
-\RequirePackage{verbatim}
-
-% Variable used by begin code command
-\newlength{\py@codewidth}
-
-\renewcommand{\verbatim}{%
- \setlength{\parindent}{1cm}%
- % Calculate the text width for the minipage:
- \setlength{\py@codewidth}{\linewidth}%
- \addtolength{\py@codewidth}{-\parindent}%
- %
- \par\indent%
- \begin{minipage}[t]{\py@codewidth}%
- \small%
- \py@OldVerbatim%
-}
-\renewcommand{\endverbatim}{%
- \py@OldEndVerbatim%
- \end{minipage}%
-}
-
-% This does a similar thing for the {alltt} environment:
-\RequirePackage{alltt}
-\let\py@OldAllTT=\alltt
-\let\py@OldEndAllTT=\endalltt
-
-\renewcommand{\alltt}{%
- \setlength{\parindent}{1cm}%
- % Calculate the text width for the minipage:
- \setlength{\py@codewidth}{\linewidth}%
- \addtolength{\py@codewidth}{-\parindent}%
- %
- \par\indent%
- \begin{minipage}[t]{\py@codewidth}%
- \small%
- \py@OldAllTT%
-}
-\renewcommand{\endalltt}{%
- \py@OldEndAllTT%
- \end{minipage}%
-}
-
-
-\newcommand{\py@modulebadkey}{{--just-some-junk--}}
-
-
-%% Lots of index-entry generation support.
-
-% Command to wrap around stuff that refers to function / module /
-% attribute names in the index. Default behavior: like \code{}. To
-% just keep the index entries in the roman font, uncomment the second
-% definition; it matches O'Reilly style more.
-%
-\newcommand{\py@idxcode}[1]{\texttt{#1}}
-%\renewcommand{\py@idxcode}[1]{#1}
-
-% Command to generate two index entries (using subentries)
-\newcommand{\indexii}[2]{\index{#1!#2}\index{#2!#1}}
-
-% And three entries (using only one level of subentries)
-\newcommand{\indexiii}[3]{\index{#1!#2 #3}\index{#2!#3, #1}\index{#3!#1 #2}}
-
-% And four (again, using only one level of subentries)
-\newcommand{\indexiv}[4]{
-\index{#1!#2 #3 #4}
-\index{#2!#3 #4, #1}
-\index{#3!#4, #1 #2}
-\index{#4!#1 #2 #3}
-}
-
-% Command to generate a reference to a function, statement, keyword,
-% operator.
-\newcommand{\kwindex}[1]{\indexii{keyword}{#1@{\py@idxcode{#1}}}}
-\newcommand{\stindex}[1]{\indexii{statement}{#1@{\py@idxcode{#1}}}}
-\newcommand{\opindex}[1]{\indexii{operator}{#1@{\py@idxcode{#1}}}}
-\newcommand{\exindex}[1]{\indexii{exception}{#1@{\py@idxcode{#1}}}}
-\newcommand{\obindex}[1]{\indexii{object}{#1}}
-\newcommand{\bifuncindex}[1]{%
- \index{#1@{\py@idxcode{#1()}} (built-in function)}}
-
-% Add an index entry for a module
-\newcommand{\py@refmodule}[2]{\index{#1@{\py@idxcode{#1}} (#2module)}}
-\newcommand{\refmodindex}[1]{\py@refmodule{#1}{}}
-\newcommand{\refbimodindex}[1]{\py@refmodule{#1}{built-in }}
-\newcommand{\refexmodindex}[1]{\py@refmodule{#1}{extension }}
-\newcommand{\refstmodindex}[1]{\py@refmodule{#1}{standard }}
-
-% Refer to a module's documentation using a hyperlink of the module's
-% name, at least if we're building PDF:
-\@ifundefined{pdfannotlink}{%
- \newcommand{\refmodule}[2][\py@modulebadkey]{\module{#2}}
-}{%
- \newcommand{\refmodule}[2][\py@modulebadkey]{%
- \ifx\py@modulebadkey#1\def\py@modulekey{#2}\else\def\py@modulekey{#1}\fi%
- \py@linkToName{label-module-\py@modulekey}{\module{#2}}%
- }
-}
-
-% support for the module index
-\newif\ifpy@UseModuleIndex
-\py@UseModuleIndexfalse
-
-\newcommand{\makemodindex}{
- \newwrite\modindexfile
- \openout\modindexfile=mod\jobname.idx
- \py@UseModuleIndextrue
-}
-
-% Add the defining entry for a module
-\newcommand{\py@modindex}[2]{%
- \renewcommand{\py@thismodule}{#1}
- \setindexsubitem{(in module #1)}%
- \index{#1@{\py@idxcode{#1}} (#2module)|textbf}%
- \ifpy@UseModuleIndex%
- \@ifundefined{py@modplat@\py@thismodulekey}{
- \write\modindexfile{\protect\indexentry{#1@{\texttt{#1}}}{\thepage}}%
- }{\write\modindexfile{\protect\indexentry{#1@{\texttt{#1} %
- \emph{(\py@platformof[\py@thismodulekey]{})}}}{\thepage}}%
- }
- \fi%
-}
-
-% *** XXX *** THE NEXT FOUR MACROS ARE NOW OBSOLETE !!! ***
-
-% built-in & Python modules in the main distribution
-\newcommand{\bimodindex}[1]{\py@modindex{#1}{built-in }%
- \typeout{*** MACRO bimodindex IS OBSOLETE -- USE declaremodule INSTEAD!}}
-\newcommand{\stmodindex}[1]{\py@modindex{#1}{standard }%
- \typeout{*** MACRO stmodindex IS OBSOLETE -- USE declaremodule INSTEAD!}}
-
-% Python & extension modules outside the main distribution
-\newcommand{\modindex}[1]{\py@modindex{#1}{}%
- \typeout{*** MACRO modindex IS OBSOLETE -- USE declaremodule INSTEAD!}}
-\newcommand{\exmodindex}[1]{\py@modindex{#1}{extension }%
- \typeout{*** MACRO exmodindex IS OBSOLETE -- USE declaremodule INSTEAD!}}
-
-% Additional string for an index entry
-\newif\ifpy@usingsubitem\py@usingsubitemfalse
-\newcommand{\py@indexsubitem}{}
-\newcommand{\setindexsubitem}[1]{\renewcommand{\py@indexsubitem}{ #1}%
- \py@usingsubitemtrue}
-\newcommand{\ttindex}[1]{%
- \ifpy@usingsubitem
- \index{#1@{\py@idxcode{#1}}\py@indexsubitem}%
- \else%
- \index{#1@{\py@idxcode{#1}}}%
- \fi%
-}
-\newcommand{\withsubitem}[2]{%
- \begingroup%
- \def\ttindex##1{\index{##1@{\py@idxcode{##1}} #1}}%
- #2%
- \endgroup%
-}
-
-
-% Module synopsis processing -----------------------------------------------
-%
-\newcommand{\py@thisclass}{}
-\newcommand{\py@thismodule}{}
-\newcommand{\py@thismodulekey}{}
-\newcommand{\py@thismoduletype}{}
-
-\newcommand{\py@standardIndexModule}[1]{\py@modindex{#1}{standard }}
-\newcommand{\py@builtinIndexModule}[1]{\py@modindex{#1}{built-in }}
-\newcommand{\py@extensionIndexModule}[1]{\py@modindex{#1}{extension }}
-\newcommand{\py@IndexModule}[1]{\py@modindex{#1}{}}
-
-\newif\ifpy@HaveModSynopsis \py@HaveModSynopsisfalse
-\newif\ifpy@ModSynopsisFileIsOpen \py@ModSynopsisFileIsOpenfalse
-\newif\ifpy@HaveModPlatform \py@HaveModPlatformfalse
-
-% \declaremodule[key]{type}{name}
-\newcommand{\declaremodule}[3][\py@modulebadkey]{
- \py@openModSynopsisFile
- \renewcommand{\py@thismoduletype}{#2}
- \ifx\py@modulebadkey#1
- \renewcommand{\py@thismodulekey}{#3}
- \else
- \renewcommand{\py@thismodulekey}{#1}
- \fi
- \@ifundefined{py@#2IndexModule}{%
- \typeout{*** MACRO declaremodule called with unknown module type: `#2'}
- \py@IndexModule{#3}%
- }{%
- \csname py@#2IndexModule\endcsname{#3}%
- }
- \label{module-\py@thismodulekey}
-}
-\newif\ifpy@ModPlatformFileIsOpen \py@ModPlatformFileIsOpenfalse
-\newcommand{\py@ModPlatformFilename}{\jobname.pla}
-\newcommand{\platform}[1]{
- \ifpy@ModPlatformFileIsOpen\else
- \newwrite\py@ModPlatformFile
- \openout\py@ModPlatformFile=\py@ModPlatformFilename
- \py@ModPlatformFileIsOpentrue
- \fi
-}
-\InputIfFileExists{\jobname.pla}{}{}
-\newcommand{\py@platformof}[2][\py@modulebadkey]{%
- \ifx\py@modulebadkey#1 \def\py@key{#2}%
- \else \def\py@key{#1}%
- \fi%
- \csname py@modplat@\py@key\endcsname%
-}
-\newcommand{\ignorePlatformAnnotation}[1]{}
-
-% \moduleauthor{name}{email}
-\newcommand{\moduleauthor}[2]{}
-
-% \sectionauthor{name}{email}
-\newcommand{\sectionauthor}[2]{}
-
-
-\newcommand{\py@defsynopsis}{Module has no synopsis.}
-\newcommand{\py@modulesynopsis}{\py@defsynopsis}
-\newcommand{\modulesynopsis}[1]{
- \py@HaveModSynopsistrue
- \renewcommand{\py@modulesynopsis}{#1}
-}
-
-% define the file
-\newwrite\py@ModSynopsisFile
-
-% hacked from \addtocontents from latex.ltx:
-\long\def\py@writeModSynopsisFile#1{%
- \protected@write\py@ModSynopsisFile%
- {\let\label\@gobble \let\index\@gobble \let\glossary\@gobble}%
- {\string#1}%
-}
-\newcommand{\py@closeModSynopsisFile}{
- \ifpy@ModSynopsisFileIsOpen
- \closeout\py@ModSynopsisFile
- \py@ModSynopsisFileIsOpenfalse
- \fi
-}
-\newcommand{\py@openModSynopsisFile}{
- \ifpy@ModSynopsisFileIsOpen\else
- \openout\py@ModSynopsisFile=\py@ModSynopsisFilename
- \py@ModSynopsisFileIsOpentrue
- \fi
-}
-
-\newcommand{\py@ProcessModSynopsis}{
- \ifpy@HaveModSynopsis
- \py@writeModSynopsisFile{\modulesynopsis%
- {\py@thismodulekey}{\py@thismodule}%
- {\py@thismoduletype}{\py@modulesynopsis}}%
- \py@HaveModSynopsisfalse
- \fi
- \renewcommand{\py@modulesynopsis}{\py@defsynopsis}
-}
-\AtEndDocument{\py@ProcessModSynopsis\py@closeModSynopsisFile}
-
-
-\long\def\py@writeModPlatformFile#1{%
- \protected@write\py@ModPlatformFile%
- {\let\label\@gobble \let\index\@gobble \let\glossary\@gobble}%
- {\string#1}%
-}
-
-
-\newcommand{\localmoduletable}{
- \IfFileExists{\py@ModSynopsisFilename}{
- \begin{synopsistable}
- \input{\py@ModSynopsisFilename}
- \end{synopsistable}
- }{}
-}
-
-\@ifundefined{pdfoutput}{
- \newcommand{\py@ModSynopsisSummary}[4]{\bfcode{#2} & #4\\}
-}{
- \newcommand{\py@ModSynopsisSummary}[4]{%
- \py@linkToName{label-module-#1}{\bfcode{#2}} & #4\\
- }
-}
-\newenvironment{synopsistable}{
- % key, name, type, synopsis
- \let\modulesynopsis=\py@ModSynopsisSummary
- \begin{tabular}{ll}
-}{
- \end{tabular}
-}
-%
-% --------------------------------------------------------------------------
-
-
-\newcommand{\py@reset}{
- \py@usingsubitemfalse
- \py@ProcessModSynopsis
- \renewcommand{\py@thisclass}{}
- \renewcommand{\py@thismodule}{}
- \renewcommand{\py@thismodulekey}{}
- \renewcommand{\py@thismoduletype}{}
-}
-
-% Augment the sectioning commands used to get our own font family in place,
-% and reset some internal data items:
-\renewcommand{\section}{\py@reset%
- \@startsection{section}{1}{\z@}%
- {-3.5ex \@plus -1ex \@minus -.2ex}%
- {2.3ex \@plus.2ex}%
- {\reset@font\Large\py@HeaderFamily}}
-\renewcommand{\subsection}{\@startsection{subsection}{2}{\z@}%
- {-3.25ex\@plus -1ex \@minus -.2ex}%
- {1.5ex \@plus .2ex}%
- {\reset@font\large\py@HeaderFamily}}
-\renewcommand{\subsubsection}{\@startsection{subsubsection}{3}{\z@}%
- {-3.25ex\@plus -1ex \@minus -.2ex}%
- {1.5ex \@plus .2ex}%
- {\reset@font\normalsize\py@HeaderFamily}}
-\renewcommand{\paragraph}{\@startsection{paragraph}{4}{\z@}%
- {3.25ex \@plus1ex \@minus.2ex}%
- {-1em}%
- {\reset@font\normalsize\py@HeaderFamily}}
-\renewcommand{\subparagraph}{\@startsection{subparagraph}{5}{\parindent}%
- {3.25ex \@plus1ex \@minus .2ex}%
- {-1em}%
- {\reset@font\normalsize\py@HeaderFamily}}
-
-
-% This gets the underscores closer to the right width; the only change
-% from standard LaTeX is the width specified.
-
-\DeclareTextCommandDefault{\textunderscore}{%
- \leavevmode \kern.06em\vbox{\hrule\@width.55em}}
-
-% Underscore hack (only act like subscript operator if in math mode)
-%
-% The following is due to Mark Wooding (the old version didn't work with
-% Latex 2e.
-
-\DeclareRobustCommand\hackscore{%
- \ifmmode_\else\textunderscore\fi%
-}
-\begingroup
-\catcode`\_\active
-\def\next{%
- \AtBeginDocument{\catcode`\_\active\def_{\hackscore{}}}%
-}
-\expandafter\endgroup\next
-
-
-% Now for a lot of semantically-loaded environments that do a ton of magical
-% things to get the right formatting and index entries for the stuff in
-% Python modules and C API.
-
-
-% {fulllineitems} is used in one place in libregex.tex, but is really for
-% internal use in this file.
-%
-\newcommand{\py@itemnewline}[1]{%
- \@tempdima\linewidth%
- \advance\@tempdima \leftmargin\makebox[\@tempdima][l]{#1}%
-}
-
-\newenvironment{fulllineitems}{
- \begin{list}{}{\labelwidth \leftmargin \labelsep 0pt
- \rightmargin 0pt \topsep -\parskip \partopsep \parskip
- \itemsep -\parsep
- \let\makelabel=\py@itemnewline}
-}{\end{list}}
-
-% \optional is mostly for use in the arguments parameters to the various
-% {*desc} environments defined below, but may be used elsewhere. Known to
-% be used in the debugger chapter.
-%
-% Typical usage:
-%
-% \begin{funcdesc}{myfunc}{reqparm\optional{, optparm}}
-% ^^^ ^^^
-% No space here No space here
-%
-% When a function has multiple optional parameters, \optional should be
-% nested, not chained. This is right:
-%
-% \begin{funcdesc}{myfunc}{\optional{parm1\optional{, parm2}}}
-%
-\let\py@badkey=\@undefined
-
-\newcommand{\optional}[1]{%
- {\textnormal{\Large[}}{#1}\hspace{0.5mm}{\textnormal{\Large]}}}
-
-% This can be used when a function or method accepts an varying number
-% of arguments, such as by using the *args syntax in the parameter list.
-\newcommand{\py@moreargs}{...}
-
-% This can be used when you don't want to document the parameters to a
-% function or method, but simply state that it's an alias for
-% something else.
-\newcommand{\py@unspecified}{...}
-
-% C functions ------------------------------------------------------------
-% \begin{cfuncdesc}[refcount]{type}{name}{arglist}
-% Note that the [refcount] slot should only be filled in by
-% tools/anno-api.py; it pulls the value from the refcounts database.
-\newenvironment{cfuncdesc}[4][\py@badkey]{
- \begin{fulllineitems}
- \item[\code{#2 \bfcode{#3}(\py@varvars{#4})}\index{#3@{\py@idxcode{#3()}}}]
- \ifx#1\@undefined\else%
- \emph{Return value: \textbf{#1}.}\\
- \fi
-}{\end{fulllineitems}}
-
-% C variables ------------------------------------------------------------
-% \begin{cvardesc}{type}{name}
-\newenvironment{cvardesc}[2]{
- \begin{fulllineitems}
- \item[\code{#1 \bfcode{#2}}\index{#2@{\py@idxcode{#2}}}]
-}{\end{fulllineitems}}
-
-% C data types -----------------------------------------------------------
-% \begin{ctypedesc}[index name]{typedef name}
-\newenvironment{ctypedesc}[2][\py@badkey]{
- \begin{fulllineitems}
- \item[\bfcode{#2}%
- \ifx#1\@undefined%
- \index{#2@{\py@idxcode{#2}} (C type)}
- \else%
- \index{#2@{\py@idxcode{#1}} (C type)}
- \fi]
-}{\end{fulllineitems}}
-
-% Funky macros -----------------------------------------------------------
-% \begin{csimplemacro}{name}
-% -- "simple" because it has no args; NOT for constant definitions!
-\newenvironment{csimplemacrodesc}[1]{
- \begin{fulllineitems}
- \item[\bfcode{#1}\index{#1@{\py@idxcode{#1}} (macro)}]
-}{\end{fulllineitems}}
-
-% simple functions (not methods) -----------------------------------------
-% \begin{funcdesc}{name}{args}
-\newcommand{\funcline}[2]{%
- \funclineni{#1}{#2}%
- \index{#1@{\py@idxcode{#1()}} (in module \py@thismodule)}}
-\newenvironment{funcdesc}[2]{
- \begin{fulllineitems}
- \funcline{#1}{#2}
-}{\end{fulllineitems}}
-
-% similar to {funcdesc}, but doesn't add to the index
-\newcommand{\funclineni}[2]{\item[\code{\bfcode{#1}(\py@varvars{#2})}]}
-\newenvironment{funcdescni}[2]{
- \begin{fulllineitems}
- \funclineni{#1}{#2}
-}{\end{fulllineitems}}
-
-% classes ----------------------------------------------------------------
-% \begin{classdesc}{name}{constructor args}
-\newenvironment{classdesc}[2]{
- % Using \renewcommand doesn't work for this, for unknown reasons:
- \global\def\py@thisclass{#1}
- \begin{fulllineitems}
- \item[\strong{class }\code{\bfcode{#1}(\py@varvars{#2})}%
- \index{#1@{\py@idxcode{#1}} (class in \py@thismodule)}]
-}{\end{fulllineitems}}
-
-% \begin{excclassdesc}{name}{constructor args}
-% but indexes as an exception
-\newenvironment{excclassdesc}[2]{
- % Using \renewcommand doesn't work for this, for unknown reasons:
- \global\def\py@thisclass{#1}
- \begin{fulllineitems}
- \item[\strong{exception }\code{\bfcode{#1}(\py@varvars{#2})}%
- \index{#1@{\py@idxcode{#1}} (exception in \py@thismodule)}]
-}{\end{fulllineitems}}
-
-
-\let\py@classbadkey=\@undefined
-
-% object method ----------------------------------------------------------
-% \begin{methoddesc}[classname]{methodname}{args}
-\newcommand{\methodline}[3][\@undefined]{
- \methodlineni{#2}{#3}
- \ifx#1\@undefined
- \index{#2@{\py@idxcode{#2()}} (\py@thisclass\ method)}
- \else
- \index{#2@{\py@idxcode{#2()}} (#1 method)}
- \fi
-}
-\newenvironment{methoddesc}[3][\@undefined]{
- \begin{fulllineitems}
- \ifx#1\@undefined
- \methodline{#2}{#3}
- \else
- \def\py@thisclass{#1}
- \methodline{#2}{#3}
- \fi
-}{\end{fulllineitems}}
-
-% similar to {methoddesc}, but doesn't add to the index
-% (never actually uses the optional argument)
-\newcommand{\methodlineni}[3][\py@classbadkey]{%
- \item[\code{\bfcode{#2}(\py@varvars{#3})}]}
-\newenvironment{methoddescni}[3][\py@classbadkey]{
- \begin{fulllineitems}
- \methodlineni{#2}{#3}
-}{\end{fulllineitems}}
-
-% object data attribute --------------------------------------------------
-% \begin{memberdesc}[classname]{membername}
-\newcommand{\memberline}[2][\py@classbadkey]{%
- \ifx#1\@undefined
- \memberlineni{#2}
- \index{#2@{\py@idxcode{#2}} (\py@thisclass\ attribute)}
- \else
- \memberlineni{#2}
- \index{#2@{\py@idxcode{#2}} (#1 attribute)}
- \fi
-}
-\newenvironment{memberdesc}[2][\py@classbadkey]{
- \begin{fulllineitems}
- \ifx#1\@undefined
- \memberline{#2}
- \else
- \def\py@thisclass{#1}
- \memberline{#2}
- \fi
-}{\end{fulllineitems}}
-
-% similar to {memberdesc}, but doesn't add to the index
-% (never actually uses the optional argument)
-\newcommand{\memberlineni}[2][\py@classbadkey]{\item[\bfcode{#2}]}
-\newenvironment{memberdescni}[2][\py@classbadkey]{
- \begin{fulllineitems}
- \memberlineni{#2}
-}{\end{fulllineitems}}
-
-% For exceptions: --------------------------------------------------------
-% \begin{excdesc}{name}
-% -- for constructor information, use excclassdesc instead
-\newenvironment{excdesc}[1]{
- \begin{fulllineitems}
- \item[\strong{exception }\bfcode{#1}%
- \index{#1@{\py@idxcode{#1}} (exception in \py@thismodule)}]
-}{\end{fulllineitems}}
-
-% Module data or constants: ----------------------------------------------
-% \begin{datadesc}{name}
-\newcommand{\dataline}[1]{%
- \datalineni{#1}\index{#1@{\py@idxcode{#1}} (data in \py@thismodule)}}
-\newenvironment{datadesc}[1]{
- \begin{fulllineitems}
- \dataline{#1}
-}{\end{fulllineitems}}
-
-% similar to {datadesc}, but doesn't add to the index
-\newcommand{\datalineni}[1]{\item[\bfcode{#1}]\nopagebreak}
-\newenvironment{datadescni}[1]{
- \begin{fulllineitems}
- \datalineni{#1}
-}{\end{fulllineitems}}
-
-% bytecode instruction ---------------------------------------------------
-% \begin{opcodedesc}{name}{var}
-% -- {var} may be {}
-\newenvironment{opcodedesc}[2]{
- \begin{fulllineitems}
- \item[\bfcode{#1}\quad\var{#2}]
-}{\end{fulllineitems}}
-
-
-\newcommand{\nodename}[1]{\label{#1}}
-
-% For these commands, use \command{} to get the typography right, not
-% {\command}. This works better with the texinfo translation.
-\newcommand{\ABC}{{\sc abc}}
-\newcommand{\UNIX}{{\sc Unix}}
-\newcommand{\POSIX}{POSIX}
-\newcommand{\ASCII}{{\sc ascii}}
-\newcommand{\Cpp}{C\protect\raisebox{.18ex}{++}}
-\newcommand{\C}{C}
-\newcommand{\EOF}{{\sc eof}}
-\newcommand{\NULL}{\constant{NULL}}
-
-% Also for consistency: spell Python "Python", not "python"!
-
-% code is the most difficult one...
-\newcommand{\code}[1]{\textrm{\@vobeyspaces\@noligs\def\{{\char`\{}\def\}{\char`\}}\def\~{\char`\~}\def\^{\char`\^}\def\e{\char`\\}\def\${\char`\$}\def\#{\char`\#}\def\&{\char`\&}\def\%{\char`\%}%
-\texttt{#1}}}
-
-\newcommand{\bfcode}[1]{\code{\bfseries#1}} % bold-faced code font
-\newcommand{\kbd}[1]{\code{#1}}
-\newcommand{\samp}[1]{`\code{#1}'}
-% This weird definition of \var{} allows it to always appear in roman
-% italics, and won't get funky in code fragments when we play around
-% with fonts. This also works directly in math mode.
-\newcommand{\var}[1]{%
- \ifmmode%
- \hbox{\normalsize\textrm{\textit{#1\/}}}%
- \else%
- \normalsize\textrm{\textit{#1\/}}%
- \fi%
-}
-\renewcommand{\emph}[1]{{\em #1}}
-\newcommand{\dfn}[1]{\emph{#1}}
-\newcommand{\strong}[1]{{\bf #1}}
-% let's experiment with a new font:
-\newcommand{\file}[1]{`{\small\textsf{#1}}'}
-\newcommand{\filenq}[1]{{\small\textsf{#1}}}
-
-% Use this def/redef approach for \url{} since hyperref defined this already,
-% but only if we actually used hyperref:
-\@ifundefined{pdfannotlink}{
- \newcommand{\py@url}[1]{\mbox{\small\textsf{#1}}}
-}{
- \newcommand{\py@url}[1]{{%
- \pdfannotlink attr{/Border [0 0 0]} user{/S /URI /URI (#1)}%
- \py@LinkColor% color of the link text
- \mbox{\small\textsf{#1}}%
- \py@NormalColor% Turn it back off; these are declarative
- \pdfendlink}% and don't appear bound to the current
- }% formatting "box".
-}
-\let\url=\py@url
-\newcommand{\email}[1]{{\small\textsf{#1}}}
-\newcommand{\newsgroup}[1]{{\small\textsf{#1}}}
-
-\newcommand{\py@varvars}[1]{{%
- {\let\unspecified=\py@unspecified%
- \let\moreargs=\py@moreargs%
- \var{#1}}}}
-
-% I'd really like to get rid of this!
-\newif\iftexi\texifalse
-
-% This is used to get l2h to put the copyright and abstract on
-% a separate HTML page.
-\newif\ifhtml\htmlfalse
-
-
-% These should be used for all references to identifiers which are
-% used to refer to instances of specific language constructs. See the
-% names for specific semantic assignments.
-%
-% For now, don't do anything really fancy with them; just use them as
-% logical markup. This might change in the future.
-%
-\newcommand{\module}[1]{\texttt{#1}}
-\newcommand{\keyword}[1]{\texttt{#1}}
-\newcommand{\exception}[1]{\texttt{#1}}
-\newcommand{\class}[1]{\texttt{#1}}
-\newcommand{\function}[1]{\texttt{#1}}
-\newcommand{\member}[1]{\texttt{#1}}
-\newcommand{\method}[1]{\texttt{#1}}
-
-\newcommand{\pytype}[1]{#1} % built-in Python type
-
-\newcommand{\cfunction}[1]{\texttt{#1}}
-\newcommand{\ctype}[1]{\texttt{#1}} % C struct or typedef name
-\newcommand{\cdata}[1]{\texttt{#1}} % C variable, typically global
-
-\newcommand{\mimetype}[1]{{\small\textsf{#1}}}
-% The \! is a "negative thin space" in math mode.
-\newcommand{\regexp}[1]{%
- {\tiny$^{^\lceil}\!\!$%
- {\normalsize\code{#1}}%
- $\!\rfloor\!$%
- }}
-\newcommand{\envvar}[1]{%
- #1%
- \index{#1@{#1}}%
- \index{environment variables!{#1}}%
-}
-\newcommand{\makevar}[1]{#1} % variable in a Makefile
-\newcommand{\character}[1]{\samp{#1}}
-
-% constants defined in Python modules or C headers, not language constants:
-\newcommand{\constant}[1]{\code{#1}} % manifest constant, not syntactic
-
-\newcommand{\manpage}[2]{{\emph{#1}(#2)}}
-\newcommand{\pep}[1]{PEP #1\index{Python Enhancement Proposals!PEP #1}}
-\newcommand{\rfc}[1]{RFC #1\index{RFC!RFC #1}}
-\newcommand{\program}[1]{\strong{#1}}
-\newcommand{\programopt}[1]{\strong{#1}}
-% Note that \longprogramopt provides the '--'!
-\newcommand{\longprogramopt}[1]{\strong{-{}-#1}}
-
-% cited titles: \citetitle{Title of Work}
-% online: \citetitle[url-to-resource]{Title of Work}
-\newcommand{\citetitle}[2][URL]{\emph{#2}}
-
-
-% Deprecation stuff.
-% Should be extended to allow an index / list of deprecated stuff. But
-% there's a lot of stuff that needs to be done to make that automatable.
-%
-% First parameter is the release number that deprecates the feature, the
-% second is the action the should be taken by users of the feature.
-%
-% Example:
-% \deprecated{1.5.1}{Use \method{frobnicate()} instead.}
-%
-\newcommand{\deprecated}[2]{%
- \strong{Deprecated since release #1.} #2\par}
-
-% New stuff.
-% This should be used to mark things which have been added to the
-% development tree but that aren't in the release, but are documented.
-% This allows release of documentation that already includes updated
-% descriptions. Place at end of descriptor environment.
-%
-% Example:
-% \versionadded{1.5.2}
-% \versionchanged[short explanation]{2.0}
-%
-\newcommand{\versionadded}[1]{%
- { New in version #1. }}
-\newcommand{\versionchanged}[2][\py@badkey]{%
- \ifx#1\@undefined%
- { Changed in version #2. }%
- \else%
- { Changed in version #2:\ #1. }%
- \fi%
-}
-
-
-% Tables.
-%
-\newenvironment{tableii}[4]{%
- \begin{center}%
- \def\lineii##1##2{\csname#2\endcsname{##1}&##2\\}%
- \begin{tabular}{#1}\strong{#3}&\strong{#4} \\* \hline%
-}{%
- \end{tabular}%
- \end{center}%
-}
-
-\newenvironment{longtableii}[4]{%
- \begin{center}%
- \def\lineii##1##2{\csname#2\endcsname{##1}&##2\\}%
- \begin{longtable}[c]{#1}\strong{#3}&\strong{#4} \\* \hline\endhead%
-}{%
- \end{longtable}%
- \end{center}%
-}
-
-\newenvironment{tableiii}[5]{%
- \begin{center}%
- \def\lineiii##1##2##3{\csname#2\endcsname{##1}&##2&##3\\}%
- \begin{tabular}{#1}\strong{#3}&\strong{#4}&\strong{#5} \\%
- \hline%
-}{%
- \end{tabular}%
- \end{center}%
-}
-
-\newenvironment{longtableiii}[5]{%
- \begin{center}%
- \def\lineiii##1##2##3{\csname#2\endcsname{##1}&##2&##3\\}%
- \begin{longtable}[c]{#1}\strong{#3}&\strong{#4}&\strong{#5} \\%
- \hline\endhead%
-}{%
- \end{longtable}%
- \end{center}%
-}
-
-\newenvironment{tableiv}[6]{%
- \begin{center}%
- \def\lineiv##1##2##3##4{\csname#2\endcsname{##1}&##2&##3&##4\\}%
- \begin{tabular}{#1}\strong{#3}&\strong{#4}&\strong{#5}&\strong{#6} \\%
- \hline%
-}{%
- \end{tabular}%
- \end{center}%
-}
-
-\newenvironment{longtableiv}[6]{%
- \begin{center}%
- \def\lineiv##1##2##3##4{\csname#2\endcsname{##1}&##2&##3&##4\\}%
- \begin{longtable}[c]{#1}\strong{#3}&\strong{#4}&\strong{#5}&\strong{#6}%
- \\%
- \hline\endhead%
-}{%
- \end{longtable}%
- \end{center}%
-}
-
-% Cross-referencing (AMK, new impl. FLD)
-% Sample usage:
-% \begin{seealso}
-% \seemodule{rand}{Uniform random number generator.}; % Module xref
-% \seetext{\emph{Encyclopedia Britannica}}. % Ref to a book
-%
-% % A funky case: module name contains '_'; have to supply an optional key
-% \seemodule[copyreg]{copy_reg}{Interface constructor registration for
-% \module{pickle}.}
-% \end{seealso}
-%
-% Note that the last parameter for \seemodule and \seetext should be complete
-% sentences and be terminated with the proper punctuation.
-
-\@ifundefined{pdfannotlink}{%
- \newcommand{\py@seemodule}[3][\py@modulebadkey]{%
- \par%
- \ifx\py@modulebadkey#1\def\py@modulekey{#2}\else\def\py@modulekey{#1}\fi%
- \begin{fulllineitems}
- \item[Module \module{#2} (section \ref{module-\py@modulekey}):]
- #3
- \end{fulllineitems}
- }
-}{\newcommand{\py@seemodule}[3][\py@modulebadkey]{%
- \par%
- \ifx\py@modulebadkey#1\def\py@modulekey{#2}\else\def\py@modulekey{#1}\fi%
- \begin{fulllineitems}
- \item[\py@linkToName{label-module-\py@modulekey}{Module \module{#2}}
- (section \ref{module-\py@modulekey}):]
- #3
- \end{fulllineitems}
- }
-}
-% \seetitle[url]{title}{why it's interesting}
-\newcommand{\py@seetitle}[3][\py@modulebadkey]{%
- \par
- \begin{fulllineitems}
- \item[\citetitle{#2}]
- \ifx\py@modulebadkey#1\else
- \item[{\small{(\url{#1})}}]
- \fi
- #3
- \end{fulllineitems}
-}
-% \seepep{number}{title}{why it's interesting}
-\newcommand{\py@seepep}[3]{%
- \par%
- \begin{fulllineitems}
- \item[\pep{#1}, ``\emph{#2}'']
- #3
- \end{fulllineitems}
-}
-% \seerfc{number}{title}{why it's interesting}
-\newcommand{\py@seerfc}[3]{%
- \par%
- \begin{fulllineitems}
- \item[\rfc{#1}, ``\emph{#2}'']
- #3
- \end{fulllineitems}
-}
-% \seeurl{url}{why it's interesting}
-\newcommand{\py@seeurl}[2]{%
- \par%
- \begin{fulllineitems}
- \item[\url{#1}]
- #2
- \end{fulllineitems}
-}
-\newenvironment{seealso}[0]{
- \par
- \strong{See Also:}\par
- \def\seetext##1{\par{##1}}
- \let\seemodule=\py@seemodule
- \let\seepep=\py@seepep
- \let\seerfc=\py@seerfc
- \let\seetitle=\py@seetitle
- \let\seeurl=\py@seeurl
-}{\par}
-
-
-% Allow the Python release number to be specified independently of the
-% \date{}. This allows the date to reflect the document's date and
-% release to specify the Python release that is documented.
-%
-\newcommand{\py@release}{}
-\newcommand{\version}{}
-\newcommand{\shortversion}{}
-\newcommand{\releasename}{Release}
-\newcommand{\release}[1]{%
- \renewcommand{\py@release}{\releasename\space\version}%
- \renewcommand{\version}{#1}}
-\newcommand{\setshortversion}[1]{%
- \renewcommand{\shortversion}{#1}}
-
-% Allow specification of the author's address separately from the
-% author's name. This can be used to format them differently, which
-% is a good thing.
-%
-\newcommand{\py@authoraddress}{}
-\newcommand{\authoraddress}[1]{\renewcommand{\py@authoraddress}{#1}}
-\let\developersaddress=\authoraddress
-\let\developer=\author
-\let\developers=\author
-
-% This sets up the fancy chapter headings that make the documents look
-% at least a little better than the usual LaTeX output.
-%
-\@ifundefined{ChTitleVar}{}{
- \ChNameVar{\raggedleft\normalsize\py@HeaderFamily}
- \ChNumVar{\raggedleft \bfseries\Large\py@HeaderFamily}
- \ChTitleVar{\raggedleft \rm\Huge\py@HeaderFamily}
- % This creates chapter heads without the leading \vspace*{}:
- \def\@makechapterhead#1{%
- {\parindent \z@ \raggedright \normalfont
- \ifnum \c@secnumdepth >\m@ne
- \DOCH
- \fi
- \interlinepenalty\@M
- \DOTI{#1}
- }
- }
-}
-
-
-% Definition lists; requested by AMK for HOWTO documents. Probably useful
-% elsewhere as well, so keep in in the general style support.
-%
-\newenvironment{definitions}{%
- \begin{description}%
- \def\term##1{\item[##1]\mbox{}\\*[0mm]}
-}{%
- \end{description}%
-}
-
-% Tell TeX about pathological hyphenation cases:
-\hyphenation{Base-HTTP-Re-quest-Hand-ler}
+++ /dev/null
-\label{reporting-bugs}
-
-Python is a mature programming language which has established a
-reputation for stability. In order to maintain this reputation, the
-developers would like to know of any deficiencies you find in Python
-or its documentation.
-
-All bug reports should be submitted via the Python Bug Tracker on
-SourceForge (\url{http://sourceforge.net/bugs/?group_id=5470}). The
-bug tracker offers a Web form which allows pertinent information to be
-entered and submitted to the developers.
-
-Before submitting a report, please log into SourceForge if you are a
-member; this will make it possible for the developers to contact you
-for additional information if needed. If you are not a SourceForge
-member but would not mind the developers contacting you, you may
-include your email address in your bug description. In this case,
-please realize that the information is publically available and cannot
-be protected.
-
-The first step in filing a report is to determine whether the problem
-has already been reported. The advantage in doing so, aside from
-saving the developers time, is that you learn what has been done to
-fix it; it may be that the problem has already been fixed for the next
-release, or additional information is needed (in which case you are
-welcome to provide it if you can!). To do this, search the bug
-database using the search box near the bottom of the page.
-
-If the problem you're reporting is not already in the bug tracker, go
-back to the Python Bug Tracker
-(\url{http://sourceforge.net/bugs/?group_id=5470}). Select the
-``Submit a Bug'' link at the top of the page to open the bug reporting
-form.
-
-The submission form has a number of fields. The only fields that are
-required are the ``Summary'' and ``Details'' fields. For the summary,
-enter a \emph{very} short description of the problem; less than ten
-words is good. In the Details field, describe the problem in detail,
-including what you expected to happen and what did happen. Be sure to
-include the version of Python you used, whether any extension modules
-were involved, and what hardware and software platform you were using
-(including version information as appropriate).
-
-The only other field that you may want to set is the ``Category''
-field, which allows you to place the bug report into a broad category
-(such as ``Documentation'' or ``Library'').
-
-Each bug report will be assigned to a developer who will determine
-what needs to be done to correct the problem. If you have a
-SourceForge account and logged in to report the problem, you will
-receive an update each time action is taken on the bug.
-
-
-\begin{seealso}
- \seetitle[http://www-mice.cs.ucl.ac.uk/multimedia/software/documentation/ReportingBugs.html]{How
- to Report Bugs Effectively}{Article which goes into some
- detail about how to create a useful bug report. This
- describes what kind of information is useful and why it is
- useful.}
-
- \seetitle[http://www.mozilla.org/quality/bug-writing-guidelines.html]{Bug
- Writing Guidelines}{Information about writing a good bug
- report. Some of this is specific to the Mozilla project, but
- describes general good practices.}
-\end{seealso}
+++ /dev/null
-#! /usr/bin/env python
-
-"""Convert a LaTeX .toc file to some PDFTeX magic to create that neat outline.
-
-The output file has an extension of '.bkm' instead of '.out', since hyperref
-already uses that extension.
-"""
-
-import getopt
-import os
-import re
-import string
-import sys
-
-
-# Ench item in an entry is a tuple of:
-#
-# Section #, Title String, Page #, List of Sub-entries
-#
-# The return value of parse_toc() is such a tuple.
-
-cline_re = r"""^
-\\contentsline\ \{([a-z]*)} # type of section in $1
-\{(?:\\numberline\ \{([0-9.A-Z]+)})? # section number
-(.*)} # title string
-\{(\d+)}$""" # page number
-
-cline_rx = re.compile(cline_re, re.VERBOSE)
-
-OUTER_TO_INNER = -1
-
-_transition_map = {
- ('chapter', 'section'): OUTER_TO_INNER,
- ('section', 'subsection'): OUTER_TO_INNER,
- ('subsection', 'subsubsection'): OUTER_TO_INNER,
- ('subsubsection', 'subsection'): 1,
- ('subsection', 'section'): 1,
- ('section', 'chapter'): 1,
- ('subsection', 'chapter'): 2,
- ('subsubsection', 'section'): 2,
- ('subsubsection', 'chapter'): 3,
- }
-
-INCLUDED_LEVELS = ("chapter", "section", "subsection", "subsubsection")
-
-
-def parse_toc(fp, bigpart=None):
- toc = top = []
- stack = [toc]
- level = bigpart or 'chapter'
- lineno = 0
- while 1:
- line = fp.readline()
- if not line:
- break
- lineno = lineno + 1
- m = cline_rx.match(line)
- if m:
- stype, snum, title, pageno = m.group(1, 2, 3, 4)
- title = clean_title(title)
- entry = (stype, snum, title, string.atoi(pageno), [])
- if stype == level:
- toc.append(entry)
- else:
- if stype not in INCLUDED_LEVELS:
- # we don't want paragraphs & subparagraphs
- continue
- direction = _transition_map[(level, stype)]
- if direction == OUTER_TO_INNER:
- toc = toc[-1][-1]
- stack.insert(0, toc)
- toc.append(entry)
- else:
- for i in range(direction):
- del stack[0]
- toc = stack[0]
- toc.append(entry)
- level = stype
- else:
- sys.stderr.write("l.%s: " + line)
- return top
-
-
-hackscore_rx = re.compile(r"\\hackscore\s*{[^}]*}")
-raisebox_rx = re.compile(r"\\raisebox\s*{[^}]*}")
-title_rx = re.compile(r"\\([a-zA-Z])+\s+")
-title_trans = string.maketrans("", "")
-
-def clean_title(title):
- title = raisebox_rx.sub("", title)
- title = hackscore_rx.sub(r"\\_", title)
- pos = 0
- while 1:
- m = title_rx.search(title, pos)
- if m:
- start = m.start()
- if title[start:start+15] != "\\textunderscore":
- title = title[:start] + title[m.end():]
- pos = start + 1
- else:
- break
- title = string.translate(title, title_trans, "{}")
- return title
-
-
-def write_toc(toc, fp):
- for entry in toc:
- write_toc_entry(entry, fp, 0)
-
-def write_toc_entry(entry, fp, layer):
- stype, snum, title, pageno, toc = entry
- s = "\\pdfoutline goto name{page%03d}" % pageno
- if toc:
- s = "%s count -%d" % (s, len(toc))
- if snum:
- title = "%s %s" % (snum, title)
- s = "%s {%s}\n" % (s, title)
- fp.write(s)
- for entry in toc:
- write_toc_entry(entry, fp, layer + 1)
-
-
-def process(ifn, ofn, bigpart=None):
- toc = parse_toc(open(ifn), bigpart)
- write_toc(toc, open(ofn, "w"))
-
-
-def main():
- bigpart = None
- opts, args = getopt.getopt(sys.argv[1:], "c:")
- if opts:
- bigpart = opts[0][1]
- if not args:
- usage()
- sys.exit(2)
- for filename in args:
- base, ext = os.path.splitext(filename)
- ext = ext or ".toc"
- process(base + ext, base + ".bkm", bigpart)
-
-
-if __name__ == "__main__":
- main()
+++ /dev/null
-#! /bin/sh
-
-# Script which installs a development snapshot of the documentation
-# into the "Python @ SourceForge" website.
-#
-# The push-docs.sh script pushes this to the SourceForge when needed
-# and removes it when done.
-
-if [ -z "$HOME" ] ; then
- HOME=`grep fdrake /etc/passwd | sed 's|^.*:\([^:]*\):[^:]*$|\1|'`
- export HOME
-fi
-
-UPDATES="$HOME/tmp/$1"
-
-cd /home/groups/python/htdocs || exit $?
-rm -rf devel-docs || exit $?
-mkdir devel-docs || exit $?
-cd devel-docs || exit $?
-(bzip2 -dc "$UPDATES" | tar xf -) || exit $?
-rm "$UPDATES" || exit $?
+++ /dev/null
-#!/bin/sh
-sed -n 's%^\\input{\(lib[a-zA-Z0-9_]*\)}.*%../lib/\1.tex%p' ../lib/lib.tex
+++ /dev/null
-I've finally gotten around to writing some examples :-)
-
-They aren't many, but at least it's something. If you write any, feel free to
-send them to me and I will add themn.
-
-
-certgen.py - Certificate generation module
-==========================================
-
-Example module with three functions:
- createKeyPair - Create a public/private key pair
- createCertRequest - Create a certificate request
- createCertificate - Create a certificate given a cert request
-In fact, I created the certificates and keys in the 'simple' directory with
-the script mk_simple_certs.py
-
-
-simple - Simple client/server example
-=====================================
-
-Start the server with
- python server.py PORT
-and start clients with
- python client.py HOST PORT
-
-The server is a simple echo server, anything a client sends, it sends back.
-
-
-proxy.py - Example of an SSL-enabled proxy
-==========================================
-
-The proxy example demonstrate how to use set_connect_state to start
-talking SSL over an already connected socket.
-
-Usage: python proxy.py server[:port] proxy[:port]
-
-Contributed by Mihai Ibanescu
-
-
-SecureXMLRPCServer.py - SSL-enabled version of SimpleXMLRPCServer
-=================================================================
-
-This acts exactly like SimpleXMLRPCServer from the standard python library,
-but uses secure connections. The technique and classes should work for any
-SocketServer style server. However, the code has not been extensively tested.
-
-Contributed by Michal Wallace
-
--- /dev/null
+========
+Examples
+========
+
+
+certgen.py -- Certificate generation module
+===========================================
+
+Example module with three functions:
+
+createKeyPair
+ Create a public/private key pair.
+
+createCertRequest
+ Create a certificate request.
+
+createCertificate
+ Create a certificate given a cert request.
+
+In fact, I created the certificates and keys in the 'simple' directory with the script ``mk_simple_certs.py``.
+
+
+simple -- Simple client/server example
+======================================
+
+Start the server with::
+
+ python server.py PORT
+
+and start clients with::
+
+ python client.py HOST PORT
+
+The server is a simple echo server, anything a client sends, it sends back.
+
+
+proxy.py -- Example of an SSL-enabled proxy
+===========================================
+
+The proxy example demonstrate how to use set_connect_state to start talking SSL over an already connected socket.
+
+Usage::
+
+ python proxy.py server[:port] proxy[:port]
+
+Contributed by Mihai Ibanescu
+
+
+SecureXMLRPCServer.py -- SSL-enabled version of SimpleXMLRPCServer
+==================================================================
+
+Acts exactly like `SimpleXMLRPCServer <https://docs.python.org/3/library/xmlrpc.server.html>`_ from the Python standard library, but uses secure connections.
+The technique and classes should work for any SocketServer style server.
+However, the code has not been extensively tested.
+
+Contributed by Michal Wallace
This code is in the public domain.
It is provided AS-IS WITH NO WARRANTY WHATSOEVER.
"""
-import SocketServer
-import os, socket
+
import SimpleXMLRPCServer
+import SocketServer
+import os
+import socket
+
from OpenSSL import SSL
+
class SSLWrapper:
"""
This whole class exists just to filter out a parameter
so I'm making a proxy instead of subclassing.
"""
self.__dict__["conn"] = conn
- def __getattr__(self,name):
+
+ def __getattr__(self, name):
return getattr(self.__dict__["conn"], name)
- def __setattr__(self,name, value):
+
+ def __setattr__(self, name, value):
setattr(self.__dict__["conn"], name, value)
+
def shutdown(self, how=1):
"""
SimpleXMLRpcServer.doPOST calls shutdown(1),
an argument. So we just discard the argument.
"""
self.__dict__["conn"].shutdown()
+
def accept(self):
"""
This is the other part of the shutdown() workaround.
return (SSLWrapper(c), a)
-
class SecureTCPServer(SocketServer.TCPServer):
"""
Just like TCPServer, but use a socket.
This really ought to let you specify the key and certificate files.
"""
def __init__(self, server_address, RequestHandlerClass):
- SocketServer.BaseServer.__init__(self, server_address, RequestHandlerClass)
+ SocketServer.BaseServer.__init__(
+ self, server_address, RequestHandlerClass
+ )
- ## Same as normal, but make it secure:
+ # Same as normal, but make it secure:
ctx = SSL.Context(SSL.SSLv23_METHOD)
ctx.set_options(SSL.OP_NO_SSLv2)
dir = os.curdir
- ctx.use_privatekey_file (os.path.join(dir, 'server.pkey'))
+ ctx.use_privatekey_file(os.path.join(dir, 'server.pkey'))
ctx.use_certificate_file(os.path.join(dir, 'server.cert'))
- self.socket = SSLWrapper(SSL.Connection(ctx, socket.socket(self.address_family,
- self.socket_type)))
+ self.socket = SSLWrapper(
+ SSL.Connection(
+ ctx, socket.socket(self.address_family, self.socket_type)
+ )
+ )
self.server_bind()
self.server_activate()
-class SecureXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
+class SecureXMLRPCRequestHandler(
+ SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
def setup(self):
"""
We need to use socket._fileobject Because SSL.Connection
doesn't have a 'dup'. Not exactly sure WHY this is, but
this is backed up by comments in socket.py and SSL/connection.c
"""
- self.connection = self.request # for doPOST
+ self.connection = self.request # for doPOST
self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
-
-class SecureXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer, SecureTCPServer):
+
+class SecureXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer,
+ SecureTCPServer):
def __init__(self, addr,
requestHandler=SecureXMLRPCRequestHandler,
logRequests=1):
self.logRequests = logRequests
self.instance = None
SecureTCPServer.__init__(self, addr, requestHandler)
-
TYPE_RSA = crypto.TYPE_RSA
TYPE_DSA = crypto.TYPE_DSA
+
def createKeyPair(type, bits):
"""
Create a public/private key pair.
pkey.generate_key(type, bits)
return pkey
-def createCertRequest(pkey, digest="md5", **name):
+
+def createCertRequest(pkey, digest="sha256", **name):
"""
Create a certificate request.
Arguments: pkey - The key to associate with the request
- digest - Digestion method to use for signing, default is md5
+ digest - Digestion method to use for signing, default is sha256
**name - The name of the subject of the request, possible
arguments are:
C - Country name
req = crypto.X509Req()
subj = req.get_subject()
- for (key,value) in name.items():
+ for key, value in name.items():
setattr(subj, key, value)
req.set_pubkey(pkey)
req.sign(pkey, digest)
return req
-def createCertificate(req, (issuerCert, issuerKey), serial, (notBefore, notAfter), digest="md5"):
+
+def createCertificate(req, issuerCertKey, serial, validityPeriod,
+ digest="sha256"):
"""
Generate a certificate given a certificate request.
- Arguments: req - Certificate reqeust to use
+ Arguments: req - Certificate request to use
issuerCert - The certificate of the issuer
issuerKey - The private key of the issuer
serial - Serial number for the certificate
starts being valid
notAfter - Timestamp (relative to now) when the certificate
stops being valid
- digest - Digest method to use for signing, default is md5
+ digest - Digest method to use for signing, default is sha256
Returns: The signed certificate in an X509 object
"""
+ issuerCert, issuerKey = issuerCertKey
+ notBefore, notAfter = validityPeriod
cert = crypto.X509()
cert.set_serial_number(serial)
cert.gmtime_adj_notBefore(notBefore)
cert.set_pubkey(req.get_pubkey())
cert.sign(issuerKey, digest)
return cert
-
Create certificates and private keys for the 'simple' example.
"""
+from __future__ import print_function
+
from OpenSSL import crypto
-from certgen import * # yes yes, I know, I'm lazy
-cakey = createKeyPair(TYPE_RSA, 1024)
+from certgen import (
+ createKeyPair,
+ createCertRequest,
+ createCertificate,
+)
+
+cakey = createKeyPair(crypto.TYPE_RSA, 2048)
careq = createCertRequest(cakey, CN='Certificate Authority')
-cacert = createCertificate(careq, (careq, cakey), 0, (0, 60*60*24*365*5)) # five years
-open('simple/CA.pkey', 'w').write(crypto.dump_privatekey(crypto.FILETYPE_PEM, cakey))
-open('simple/CA.cert', 'w').write(crypto.dump_certificate(crypto.FILETYPE_PEM, cacert))
-for (fname, cname) in [('client', 'Simple Client'), ('server', 'Simple Server')]:
- pkey = createKeyPair(TYPE_RSA, 1024)
+# CA certificate is valid for five years.
+cacert = createCertificate(careq, (careq, cakey), 0, (0, 60*60*24*365*5))
+
+print('Creating Certificate Authority private key in "simple/CA.pkey"')
+with open('simple/CA.pkey', 'w') as capkey:
+ capkey.write(
+ crypto.dump_privatekey(crypto.FILETYPE_PEM, cakey).decode('utf-8')
+ )
+
+print('Creating Certificate Authority certificate in "simple/CA.cert"')
+with open('simple/CA.cert', 'w') as ca:
+ ca.write(
+ crypto.dump_certificate(crypto.FILETYPE_PEM, cacert).decode('utf-8')
+ )
+
+for (fname, cname) in [('client', 'Simple Client'),
+ ('server', 'Simple Server')]:
+ pkey = createKeyPair(crypto.TYPE_RSA, 2048)
req = createCertRequest(pkey, CN=cname)
- cert = createCertificate(req, (cacert, cakey), 1, (0, 60*60*24*365*5)) # five years
- open('simple/%s.pkey' % (fname,), 'w').write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
- open('simple/%s.cert' % (fname,), 'w').write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
+ # Certificates are valid for five years.
+ cert = createCertificate(req, (cacert, cakey), 1, (0, 60*60*24*365*5))
+
+ print('Creating Certificate %s private key in "simple/%s.pkey"'
+ % (fname, fname))
+ with open('simple/%s.pkey' % (fname,), 'w') as leafpkey:
+ leafpkey.write(
+ crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey).decode('utf-8')
+ )
+
+ print('Creating Certificate %s certificate in "simple/%s.cert"'
+ % (fname, fname))
+ with open('simple/%s.cert' % (fname,), 'w') as leafcert:
+ leafcert.write(
+ crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode('utf-8')
+ )
#!/usr/bin/env python
#
-# This script demostrates how one can use pyOpenSSL to speak SSL over an HTTP
+# This script demonstrates how one can use pyOpenSSL to speak SSL over an HTTP
# proxy
# The challenge here is to start talking SSL over an already connected socket
#
#
# $Id: proxy.py,v 1.2 2004/07/22 12:01:25 martin Exp $
-import sys, socket, string
+import sys
+import socket
+import string
+
from OpenSSL import SSL
+
def usage(exit_code=0):
print "Usage: %s server[:port] proxy[:port]" % sys.argv[0]
print " Connects SSL to the specified server (port 443 by default)"
print " using the specified proxy (port 8080 by default)"
sys.exit(exit_code)
+
def main():
# Command-line processing
if len(sys.argv) != 3:
run(split_host(server, 443), split_host(proxy, 8080))
+
def split_host(hostname, default_port=80):
a = string.split(hostname, ':', 1)
if len(a) == 1:
a.append(default_port)
return a[0], int(a[1])
-
+
# Connects to the server, through the proxy
def run(server, proxy):
print buff,
+
if __name__ == '__main__':
main()
Simple SSL client, using blocking I/O
"""
-from OpenSSL import SSL
-import sys, os, select, socket
+import os
+import socket
+import sys
+
+from OpenSSL import SSL, crypto
+
def verify_cb(conn, cert, errnum, depth, ok):
- # This obviously has to be updated
- print 'Got certificate: %s' % cert.get_subject()
+ certsubject = crypto.X509Name(cert.get_subject())
+ commonname = certsubject.commonName
+ print('Got certificate: ' + commonname)
return ok
+
if len(sys.argv) < 3:
- print 'Usage: python[2] client.py HOST PORT'
+ print('Usage: python client.py HOST PORT')
sys.exit(1)
+
dir = os.path.dirname(sys.argv[0])
if dir == '':
dir = os.curdir
+
# Initialize context
ctx = SSL.Context(SSL.SSLv23_METHOD)
-ctx.set_verify(SSL.VERIFY_PEER, verify_cb) # Demand a certificate
-ctx.use_privatekey_file (os.path.join(dir, 'client.pkey'))
+ctx.set_options(SSL.OP_NO_SSLv2)
+ctx.set_options(SSL.OP_NO_SSLv3)
+ctx.set_verify(SSL.VERIFY_PEER, verify_cb) # Demand a certificate
+ctx.use_privatekey_file(os.path.join(dir, 'client.pkey'))
ctx.use_certificate_file(os.path.join(dir, 'client.cert'))
ctx.load_verify_locations(os.path.join(dir, 'CA.cert'))
break
try:
sock.send(line)
- sys.stdout.write(sock.recv(1024))
+ sys.stdout.write(sock.recv(1024).decode('utf-8'))
sys.stdout.flush()
except SSL.Error:
- print 'Connection died unexpectedly'
+ print('Connection died unexpectedly')
break
Simple echo server, using nonblocking I/O
"""
-from OpenSSL import SSL
-import sys, os, select, socket
+from __future__ import print_function
+
+import os
+import select
+import socket
+import sys
+
+from OpenSSL import SSL, crypto
def verify_cb(conn, cert, errnum, depth, ok):
- # This obviously has to be updated
- print 'Got certificate: %s' % cert.get_subject()
+ certsubject = crypto.X509Name(cert.get_subject())
+ commonname = certsubject.commonName
+ print('Got certificate: ' + commonname)
return ok
+
if len(sys.argv) < 2:
- print 'Usage: python[2] server.py PORT'
+ print('Usage: python server.py PORT')
sys.exit(1)
dir = os.path.dirname(sys.argv[0])
# Initialize context
ctx = SSL.Context(SSL.SSLv23_METHOD)
ctx.set_options(SSL.OP_NO_SSLv2)
-ctx.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb) # Demand a certificate
-ctx.use_privatekey_file (os.path.join(dir, 'server.pkey'))
+ctx.set_options(SSL.OP_NO_SSLv3)
+ctx.set_verify(
+ SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb
+) # Demand a certificate
+ctx.use_privatekey_file(os.path.join(dir, 'server.pkey'))
ctx.use_certificate_file(os.path.join(dir, 'server.cert'))
ctx.load_verify_locations(os.path.join(dir, 'CA.cert'))
# Set up server
server = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
server.bind(('', int(sys.argv[1])))
-server.listen(3)
+server.listen(3)
server.setblocking(0)
clients = {}
writers = {}
+
def dropClient(cli, errors=None):
if errors:
- print 'Client %s left unexpectedly:' % (clients[cli],)
- print ' ', errors
+ print('Client %s left unexpectedly:' % (clients[cli],))
+ print(' ', errors)
else:
- print 'Client %s left politely' % (clients[cli],)
+ print('Client %s left politely' % (clients[cli],))
del clients[cli]
- if writers.has_key(cli):
+ if cli in writers:
del writers[cli]
if not errors:
cli.shutdown()
cli.close()
+
while 1:
try:
- r,w,_ = select.select([server]+clients.keys(), writers.keys(), [])
- except:
+ r, w, _ = select.select(
+ [server] + list(clients.keys()), list(writers.keys()), []
+ )
+ except Exception:
break
for cli in r:
if cli == server:
- cli,addr = server.accept()
- print 'Connection from %s' % (addr,)
+ cli, addr = server.accept()
+ print('Connection from %s' % (addr,))
clients[cli] = addr
else:
try:
- ret = cli.recv(1024)
- except (SSL.WantReadError, SSL.WantWriteError, SSL.WantX509LookupError):
+ ret = cli.recv(1024).decode('utf-8')
+ except (SSL.WantReadError,
+ SSL.WantWriteError,
+ SSL.WantX509LookupError):
pass
except SSL.ZeroReturnError:
dropClient(cli)
- except SSL.Error, errors:
+ except SSL.Error as errors:
dropClient(cli, errors)
else:
- if not writers.has_key(cli):
+ if cli not in writers:
writers[cli] = ''
writers[cli] = writers[cli] + ret
for cli in w:
try:
ret = cli.send(writers[cli])
- except (SSL.WantReadError, SSL.WantWriteError, SSL.WantX509LookupError):
+ except (SSL.WantReadError,
+ SSL.WantWriteError,
+ SSL.WantX509LookupError):
pass
except SSL.ZeroReturnError:
dropClient(cli)
- except SSL.Error, errors:
+ except SSL.Error as errors:
dropClient(cli, errors)
else:
writers[cli] = writers[cli][ret:]
# Copyright (C) Jean-Paul Calderone
# See LICENSE for details.
-if __name__ == '__main__':
- import client
- raise SystemExit(client.main())
-
from sys import argv, stdout
from socket import socket
from OpenSSL.SSL import TLSv1_METHOD, Context, Connection
+
def main():
"""
Connect to an SNI-enabled server and request a specific hostname, specified
print 'Server subject is', client_ssl.get_peer_certificate().get_subject()
client_ssl.close()
+
+if __name__ == '__main__':
+ import client
+ raise SystemExit(client.main())
# Copyright (C) Jean-Paul Calderone
# See LICENSE for details.
-if __name__ == '__main__':
- import server
- raise SystemExit(server.main())
-
from sys import stdout
from socket import SOL_SOCKET, SO_REUSEADDR, socket
from OpenSSL.crypto import FILETYPE_PEM, load_privatekey, load_certificate
from OpenSSL.SSL import TLSv1_METHOD, Context, Connection
+
def load(domain):
crt = open(domain + ".crt")
key = open(domain + ".key")
certificates = {
"example.invalid": load("example.invalid"),
"another.invalid": load("another.invalid"),
- }
+}
def pick_certificate(connection):
new_context.use_privatekey(key)
new_context.use_certificate(cert)
connection.set_context(new_context)
+
+
+if __name__ == '__main__':
+ raise SystemExit(main())
# published by the Open Source Initiative.
Name: python-pyOpenSSL
-Version: 0.13
+Version: 19.0.0.0
Release: 0
-Url: http://launchpad.net/pyopenssl
+Url: https://github.com/pyca/pyopenssl
Summary: Python wrapper module around the OpenSSL library
License: Apache-2.0
Group: Development/Languages/Python
-Source: http://pypi.python.org/packages/source/p/pyOpenSSL/pyOpenSSL-%{version}.tar.gz
+Source: https://files.pythonhosted.org/packages/source/p/pyOpenSSL/pyOpenSSL-%{version}.tar.gz
Source1001: python-pyOpenSSL.manifest
-BuildRequires: libopenssl-devel
+#BuildRequires: libopenssl1.1-devel
+BuildRequires: python-setuptools
BuildRequires: python-devel
-%{!?python_sitearch: %global python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")}
+BuildRequires: openssl1.1
+#comment the below line. if add the below line, compile failed, python setup.py install will use /usr/lib/python2.7 for x86_64 arch. the below is /usr/lib64/python2.7
+#%{!?python_sitearch: %global python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")}
%description
High-level wrapper around a subset of the OpenSSL library, includes
%files
%manifest %{name}.manifest
+%license LICENSE
+%doc *.rst
%defattr(-,root,root,-)
-%{python_sitearch}/*
+%{python_sitelib}/*
%changelog
-make -C doc text ps html
+make -C doc text html
-[sdist]
-force_manifest=1
+[tool:pytest]
+minversion = 3.0.1
+strict = true
+testpaths = tests
+
+[bdist_wheel]
+universal = 1
+
+[metadata]
+license_file = LICENSE
-# bdist_rpm settings contributed by Mihai Ibanescu <misa@redhat.com>
[bdist_rpm]
release = 1
-build-requires = lynx openssl-devel python-devel perl tetex-dvips tetex-latex
+build-requires = openssl-devel python-devel python-sphinx
group = Development/Libraries
build_script = rpm/build_script
-doc-files = doc/pyOpenSSL.txt doc/pyOpenSSL.ps doc/html
+doc-files = doc/_build/html
+
+[egg_info]
+tag_build =
+tag_date = 0
+
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
-# Copyright (C) AB Strakt 2001, All rights reserved
-# Copyright (C) Jean-Paul Calderone 2008-2010, All rights reserved
+# Copyright (C) Jean-Paul Calderone 2008-2015, All rights reserved
#
"""
-Installation script for the OpenSSL module
+Installation script for the OpenSSL package.
"""
-import sys, os
-from distutils.core import Extension, setup
-from distutils.errors import DistutilsFileError
-from distutils.command.build_ext import build_ext
-
-# XXX Deduplicate this
-__version__ = '0.13'
-
-crypto_src = ['OpenSSL/crypto/crypto.c', 'OpenSSL/crypto/x509.c',
- 'OpenSSL/crypto/x509name.c', 'OpenSSL/crypto/pkey.c',
- 'OpenSSL/crypto/x509store.c', 'OpenSSL/crypto/x509req.c',
- 'OpenSSL/crypto/x509ext.c', 'OpenSSL/crypto/pkcs7.c',
- 'OpenSSL/crypto/pkcs12.c', 'OpenSSL/crypto/netscape_spki.c',
- 'OpenSSL/crypto/revoked.c', 'OpenSSL/crypto/crl.c',
- 'OpenSSL/util.c']
-crypto_dep = ['OpenSSL/crypto/crypto.h', 'OpenSSL/crypto/x509.h',
- 'OpenSSL/crypto/x509name.h', 'OpenSSL/crypto/pkey.h',
- 'OpenSSL/crypto/x509store.h', 'OpenSSL/crypto/x509req.h',
- 'OpenSSL/crypto/x509ext.h', 'OpenSSL/crypto/pkcs7.h',
- 'OpenSSL/crypto/pkcs12.h', 'OpenSSL/crypto/netscape_spki.h',
- 'OpenSSL/crypto/revoked.h', 'OpenSSL/crypto/crl.h',
- 'OpenSSL/util.h']
-rand_src = ['OpenSSL/rand/rand.c', 'OpenSSL/util.c']
-rand_dep = ['OpenSSL/util.h']
-ssl_src = ['OpenSSL/ssl/connection.c', 'OpenSSL/ssl/context.c', 'OpenSSL/ssl/ssl.c',
- 'OpenSSL/util.c']
-ssl_dep = ['OpenSSL/ssl/connection.h', 'OpenSSL/ssl/context.h', 'OpenSSL/ssl/ssl.h',
- 'OpenSSL/util.h']
-
-IncludeDirs = None
-LibraryDirs = None
-
-# Add more platforms here when needed
-if os.name == 'nt' or sys.platform == 'win32':
-
- Libraries = ['Ws2_32']
-
-
-
- class BuildExtension(build_ext):
- """
- A custom command that semiautomatically finds dependencies required by
- PyOpenSSL.
- """
-
- user_options = (build_ext.user_options +
- [("with-openssl=", None,
- "directory where OpenSSL is installed")])
- with_openssl = None
- openssl_dlls = ()
- openssl_mingw = False
-
-
- def finalize_options(self):
- """
- Update build options with details about OpenSSL.
- """
- build_ext.finalize_options(self)
- if self.with_openssl is None:
- self.find_openssl()
- self.find_openssl_dlls()
- self.add_openssl_compile_info()
-
-
- def find_openssl(self):
- """
- Find OpenSSL's install directory.
- """
- potentials = []
- dirs = os.environ.get("PATH").split(os.pathsep)
- for d in dirs:
- if os.path.exists(os.path.join(d, "openssl.exe")):
- ssldir, bin = os.path.split(d)
- if not bin:
- ssldir, bin = os.path.split(ssldir)
- potentials.append(ssldir)
- childdirs = os.listdir(ssldir)
- if "lib" in childdirs and "include" in childdirs:
- self.with_openssl = ssldir
- return
- if potentials:
- raise DistutilsFileError(
- "Only found improper OpenSSL directories: %r" % (
- potentials,))
- else:
- raise DistutilsFileError("Could not find 'openssl.exe'")
-
-
- def find_openssl_dlls(self):
- """
- Find OpenSSL's shared libraries.
- """
- self.openssl_dlls = []
- self.find_openssl_dll("libssl32.dll", False)
- if self.openssl_dlls:
- self.openssl_mingw = True
- else:
- self.find_openssl_dll("ssleay32.dll", True)
- self.find_openssl_dll("libeay32.dll", True)
- # add zlib to the mix if it looks like OpenSSL
- # was linked with a private copy of it
- self.find_openssl_dll("zlib1.dll", False)
-
-
- def find_openssl_dll(self, name, required):
- """
- Find OpenSSL's shared library and its path after installation.
- """
- dllpath = os.path.join(self.with_openssl, "bin", name)
- if not os.path.exists(dllpath):
- if required:
- raise DistutilsFileError("could not find '%s'" % name)
- else:
- return
- newpath = os.path.join(self.build_lib, "OpenSSL", name)
- self.openssl_dlls.append((dllpath, newpath))
-
-
- def add_openssl_compile_info(self):
- """
- Set up various compile and link parameters.
- """
- if self.compiler == "mingw32":
- if self.openssl_mingw:
- # Library path and library names are sane when OpenSSL is
- # built with MinGW .
- libdir = "lib"
- libs = ["eay32", "ssl32"]
- else:
- libdir = ""
- libs = []
- # Unlike when using the binary installer, which creates
- # an atypical shared library name 'ssleay32', so we have
- # to use this workaround.
- if self.link_objects is None:
- self.link_objects = []
- for dllpath, _ in self.openssl_dlls:
- dllname = os.path.basename(dllpath)
- libname = os.path.splitext(dllname)[0] + ".a"
- libpath = os.path.join(self.with_openssl,
- "lib", "MinGW", libname)
- self.link_objects.append(libpath)
- else:
- libdir = "lib"
- libs = ["libeay32", "ssleay32"]
- self.include_dirs.append(os.path.join(self.with_openssl, "include"))
- self.library_dirs.append(os.path.join(self.with_openssl, libdir))
- self.libraries.extend(libs)
-
-
- def run(self):
- """
- Build extension modules and copy shared libraries.
- """
- build_ext.run(self)
- for dllpath, newpath in self.openssl_dlls:
- self.copy_file(dllpath, newpath)
-
-
- def get_outputs(self):
- """
- Return a list of file paths built by this comand.
- """
- output = [pathpair[1] for pathpair in self.openssl_dlls]
- output.extend(build_ext.get_outputs(self))
- return output
-
-
-
-else:
- Libraries = ['ssl', 'crypto']
- BuildExtension = build_ext
-
-
-
-def mkExtension(name):
- modname = 'OpenSSL.' + name
- src = globals()[name.lower() + '_src']
- dep = globals()[name.lower() + '_dep']
- return Extension(modname, src, libraries=Libraries, depends=dep,
- include_dirs=IncludeDirs, library_dirs=LibraryDirs)
-
-
-setup(name='pyOpenSSL', version=__version__,
- packages = ['OpenSSL'],
- package_dir = {'OpenSSL': 'OpenSSL'},
- ext_modules = [mkExtension('crypto'), mkExtension('rand'),
- mkExtension('SSL')],
- py_modules = ['OpenSSL.__init__', 'OpenSSL.tsafe',
- 'OpenSSL.version', 'OpenSSL.test.__init__',
- 'OpenSSL.test.util',
- 'OpenSSL.test.test_crypto',
- 'OpenSSL.test.test_rand',
- 'OpenSSL.test.test_ssl'],
- zip_safe = False,
- cmdclass = {"build_ext": BuildExtension},
- description = 'Python wrapper module around the OpenSSL library',
- author = 'Martin Sjögren, AB Strakt',
- author_email = 'msjogren@gmail.com',
- maintainer = 'Jean-Paul Calderone',
- maintainer_email = 'exarkun@twistedmatrix.com',
- url = 'http://pyopenssl.sourceforge.net/',
- license = 'APL2',
- long_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 ;)"""
- )
+import codecs
+import os
+import re
+
+from setuptools import setup, find_packages
+
+
+HERE = os.path.abspath(os.path.dirname(__file__))
+META_PATH = os.path.join("src", "OpenSSL", "version.py")
+
+
+def read_file(*parts):
+ """
+ Build an absolute path from *parts* and and return the contents of the
+ resulting file. Assume UTF-8 encoding.
+ """
+ with codecs.open(os.path.join(HERE, *parts), "rb", "ascii") as f:
+ return f.read()
+
+
+META_FILE = read_file(META_PATH)
+
+
+def find_meta(meta):
+ """
+ Extract __*meta*__ from META_FILE.
+ """
+ meta_match = re.search(
+ r"^__{meta}__ = ['\"]([^'\"]*)['\"]".format(meta=meta),
+ META_FILE, re.M
+ )
+ if meta_match:
+ return meta_match.group(1)
+ raise RuntimeError("Unable to find __{meta}__ string.".format(meta=meta))
+
+
+URI = find_meta("uri")
+LONG = (
+ read_file("README.rst") + "\n\n" +
+ "Release Information\n" +
+ "===================\n\n" +
+ re.search(r"(\d{2}.\d.\d \(.*?\)\n.*?)\n\n\n----\n",
+ read_file("CHANGELOG.rst"), re.S).group(1) +
+ "\n\n`Full changelog " +
+ "<{uri}en/stable/changelog.html>`_.\n\n"
+).format(uri=URI)
+
+
+if __name__ == "__main__":
+ setup(
+ name=find_meta("title"),
+ version=find_meta("version"),
+ description=find_meta("summary"),
+ long_description=LONG,
+ author=find_meta("author"),
+ author_email=find_meta("email"),
+ maintainer="Hynek Schlawack",
+ maintainer_email="hs@ox.cx",
+ url=URI,
+ license=find_meta("license"),
+ classifiers=[
+ 'Development Status :: 6 - Mature',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Apache Software License',
+ 'Operating System :: MacOS :: MacOS X',
+ 'Operating System :: Microsoft :: Windows',
+ 'Operating System :: POSIX',
+
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
+
+ 'Programming Language :: Python :: Implementation :: CPython',
+ 'Programming Language :: Python :: Implementation :: PyPy',
+ 'Topic :: Security :: Cryptography',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ 'Topic :: System :: Networking',
+ ],
+
+ packages=find_packages(where="src"),
+ package_dir={"": "src"},
+ install_requires=[
+ # Fix cryptographyMinimum in tox.ini when changing this!
+ "cryptography>=2.3",
+ "six>=1.5.2"
+ ],
+ extras_require={
+ "test": [
+ "flaky",
+ "pretend",
+ "pytest>=3.0.1",
+ ],
+ "docs": [
+ "sphinx",
+ "sphinx_rtd_theme",
+ ]
+ },
+ )
--- /dev/null
+import os
+import socket
+from sys import platform
+from functools import wraps, partial
+from itertools import count, chain
+from weakref import WeakValueDictionary
+from errno import errorcode
+
+from cryptography.utils import deprecated
+
+from six import (
+ binary_type as _binary_type, integer_types as integer_types, int2byte,
+ indexbytes)
+
+from OpenSSL._util import (
+ UNSPECIFIED as _UNSPECIFIED,
+ exception_from_error_queue as _exception_from_error_queue,
+ ffi as _ffi,
+ lib as _lib,
+ make_assert as _make_assert,
+ native as _native,
+ path_string as _path_string,
+ text_to_bytes_and_warn as _text_to_bytes_and_warn,
+ no_zero_allocator as _no_zero_allocator,
+)
+
+from OpenSSL.crypto import (
+ FILETYPE_PEM, _PassphraseHelper, PKey, X509Name, X509, X509Store)
+
+__all__ = [
+ 'OPENSSL_VERSION_NUMBER',
+ 'SSLEAY_VERSION',
+ 'SSLEAY_CFLAGS',
+ 'SSLEAY_PLATFORM',
+ 'SSLEAY_DIR',
+ 'SSLEAY_BUILT_ON',
+ 'SENT_SHUTDOWN',
+ 'RECEIVED_SHUTDOWN',
+ 'SSLv2_METHOD',
+ 'SSLv3_METHOD',
+ 'SSLv23_METHOD',
+ 'TLSv1_METHOD',
+ 'TLSv1_1_METHOD',
+ 'TLSv1_2_METHOD',
+ 'OP_NO_SSLv2',
+ 'OP_NO_SSLv3',
+ 'OP_NO_TLSv1',
+ 'OP_NO_TLSv1_1',
+ 'OP_NO_TLSv1_2',
+ 'MODE_RELEASE_BUFFERS',
+ 'OP_SINGLE_DH_USE',
+ 'OP_SINGLE_ECDH_USE',
+ 'OP_EPHEMERAL_RSA',
+ 'OP_MICROSOFT_SESS_ID_BUG',
+ 'OP_NETSCAPE_CHALLENGE_BUG',
+ 'OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG',
+ 'OP_SSLREF2_REUSE_CERT_TYPE_BUG',
+ 'OP_MICROSOFT_BIG_SSLV3_BUFFER',
+ 'OP_MSIE_SSLV2_RSA_PADDING',
+ 'OP_SSLEAY_080_CLIENT_DH_BUG',
+ 'OP_TLS_D5_BUG',
+ 'OP_TLS_BLOCK_PADDING_BUG',
+ 'OP_DONT_INSERT_EMPTY_FRAGMENTS',
+ 'OP_CIPHER_SERVER_PREFERENCE',
+ 'OP_TLS_ROLLBACK_BUG',
+ 'OP_PKCS1_CHECK_1',
+ 'OP_PKCS1_CHECK_2',
+ 'OP_NETSCAPE_CA_DN_BUG',
+ 'OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG',
+ 'OP_NO_COMPRESSION',
+ 'OP_NO_QUERY_MTU',
+ 'OP_COOKIE_EXCHANGE',
+ 'OP_NO_TICKET',
+ 'OP_ALL',
+ 'VERIFY_PEER',
+ 'VERIFY_FAIL_IF_NO_PEER_CERT',
+ 'VERIFY_CLIENT_ONCE',
+ 'VERIFY_NONE',
+ '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',
+ 'SSL_ST_CONNECT',
+ 'SSL_ST_ACCEPT',
+ 'SSL_ST_MASK',
+ 'SSL_CB_LOOP',
+ 'SSL_CB_EXIT',
+ 'SSL_CB_READ',
+ 'SSL_CB_WRITE',
+ 'SSL_CB_ALERT',
+ 'SSL_CB_READ_ALERT',
+ 'SSL_CB_WRITE_ALERT',
+ 'SSL_CB_ACCEPT_LOOP',
+ 'SSL_CB_ACCEPT_EXIT',
+ 'SSL_CB_CONNECT_LOOP',
+ 'SSL_CB_CONNECT_EXIT',
+ 'SSL_CB_HANDSHAKE_START',
+ 'SSL_CB_HANDSHAKE_DONE',
+ 'Error',
+ 'WantReadError',
+ 'WantWriteError',
+ 'WantX509LookupError',
+ 'ZeroReturnError',
+ 'SysCallError',
+ 'SSLeay_version',
+ 'Session',
+ 'Context',
+ 'Connection'
+]
+
+try:
+ _buffer = buffer
+except NameError:
+ class _buffer(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 = _lib.SSL_OP_NO_TLSv1_1
+OP_NO_TLSv1_2 = _lib.SSL_OP_NO_TLSv1_2
+
+MODE_RELEASE_BUFFERS = _lib.SSL_MODE_RELEASE_BUFFERS
+
+OP_SINGLE_DH_USE = _lib.SSL_OP_SINGLE_DH_USE
+OP_SINGLE_ECDH_USE = _lib.SSL_OP_SINGLE_ECDH_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
+OP_MSIE_SSLV2_RSA_PADDING = _lib.SSL_OP_MSIE_SSLV2_RSA_PADDING
+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
+)
+OP_NO_COMPRESSION = _lib.SSL_OP_NO_COMPRESSION
+
+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
+if _lib.Cryptography_HAS_SSL_ST:
+ 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
+ __all__.extend([
+ 'SSL_ST_INIT',
+ 'SSL_ST_BEFORE',
+ 'SSL_ST_OK',
+ '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
+
+# Taken from https://golang.org/src/crypto/x509/root_linux.go
+_CERTIFICATE_FILE_LOCATIONS = [
+ "/etc/ssl/certs/ca-certificates.crt", # Debian/Ubuntu/Gentoo etc.
+ "/etc/pki/tls/certs/ca-bundle.crt", # Fedora/RHEL 6
+ "/etc/ssl/ca-bundle.pem", # OpenSUSE
+ "/etc/pki/tls/cacert.pem", # OpenELEC
+ "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", # CentOS/RHEL 7
+]
+
+_CERTIFICATE_PATH_LOCATIONS = [
+ "/etc/ssl/certs", # SLES10/SLES11
+]
+
+# These values are compared to output from cffi's ffi.string so they must be
+# byte strings.
+_CRYPTOGRAPHY_MANYLINUX1_CA_DIR = b"/opt/pyca/cryptography/openssl/certs"
+_CRYPTOGRAPHY_MANYLINUX1_CA_FILE = b"/opt/pyca/cryptography/openssl/cert.pem"
+
+
+class Error(Exception):
+ """
+ An error occurred in an `OpenSSL.SSL` API.
+ """
+
+
+_raise_current_error = partial(_exception_from_error_queue, Error)
+_openssl_assert = _make_assert(Error)
+
+
+class WantReadError(Error):
+ pass
+
+
+class WantWriteError(Error):
+ pass
+
+
+class WantX509LookupError(Error):
+ pass
+
+
+class ZeroReturnError(Error):
+ pass
+
+
+class SysCallError(Error):
+ pass
+
+
+class _CallbackExceptionHelper(object):
+ """
+ A base class for wrapper classes that allow for intelligent exception
+ handling in OpenSSL callbacks.
+
+ :ivar list _problems: Any exceptions that occurred while executing in a
+ context where they could not be raised in the normal way. Typically
+ this is because OpenSSL has called into some Python code and requires a
+ return value. The exceptions are saved to be raised later when it is
+ possible to do so.
+ """
+
+ def __init__(self):
+ self._problems = []
+
+ def raise_if_problem(self):
+ """
+ Raise an exception from the OpenSSL error queue or that was previously
+ captured whe running a callback.
+ """
+ if self._problems:
+ try:
+ _raise_current_error()
+ except Error:
+ pass
+ raise self._problems.pop(0)
+
+
+class _VerifyHelper(_CallbackExceptionHelper):
+ """
+ Wrap a callback such that it can be used as a certificate verification
+ callback.
+ """
+
+ def __init__(self, callback):
+ _CallbackExceptionHelper.__init__(self)
+
+ @wraps(callback)
+ def wrapper(ok, store_ctx):
+ x509 = _lib.X509_STORE_CTX_get_current_cert(store_ctx)
+ _lib.X509_up_ref(x509)
+ cert = X509._from_raw_x509_ptr(x509)
+ error_number = _lib.X509_STORE_CTX_get_error(store_ctx)
+ error_depth = _lib.X509_STORE_CTX_get_error_depth(store_ctx)
+
+ index = _lib.SSL_get_ex_data_X509_STORE_CTX_idx()
+ ssl = _lib.X509_STORE_CTX_get_ex_data(store_ctx, index)
+ connection = Connection._reverse_mapping[ssl]
+
+ 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)
+
+
+class _NpnAdvertiseHelper(_CallbackExceptionHelper):
+ """
+ Wrap a callback such that it can be used as an NPN advertisement callback.
+ """
+
+ def __init__(self, callback):
+ _CallbackExceptionHelper.__init__(self)
+
+ @wraps(callback)
+ def wrapper(ssl, out, outlen, arg):
+ try:
+ conn = Connection._reverse_mapping[ssl]
+ protos = callback(conn)
+
+ # Join the protocols into a Python bytestring, length-prefixing
+ # each element.
+ protostr = b''.join(
+ chain.from_iterable((int2byte(len(p)), p) for p in protos)
+ )
+
+ # Save our callback arguments on the connection object. This is
+ # done to make sure that they don't get freed before OpenSSL
+ # uses them. Then, return them appropriately in the output
+ # parameters.
+ conn._npn_advertise_callback_args = [
+ _ffi.new("unsigned int *", len(protostr)),
+ _ffi.new("unsigned char[]", protostr),
+ ]
+ outlen[0] = conn._npn_advertise_callback_args[0][0]
+ out[0] = conn._npn_advertise_callback_args[1]
+ return 0
+ except Exception as e:
+ self._problems.append(e)
+ return 2 # SSL_TLSEXT_ERR_ALERT_FATAL
+
+ self.callback = _ffi.callback(
+ "int (*)(SSL *, const unsigned char **, unsigned int *, void *)",
+ wrapper
+ )
+
+
+class _NpnSelectHelper(_CallbackExceptionHelper):
+ """
+ Wrap a callback such that it can be used as an NPN selection callback.
+ """
+
+ def __init__(self, callback):
+ _CallbackExceptionHelper.__init__(self)
+
+ @wraps(callback)
+ def wrapper(ssl, out, outlen, in_, inlen, arg):
+ try:
+ conn = Connection._reverse_mapping[ssl]
+
+ # The string passed to us is actually made up of multiple
+ # length-prefixed bytestrings. We need to split that into a
+ # list.
+ instr = _ffi.buffer(in_, inlen)[:]
+ protolist = []
+ while instr:
+ length = indexbytes(instr, 0)
+ proto = instr[1:length + 1]
+ protolist.append(proto)
+ instr = instr[length + 1:]
+
+ # Call the callback
+ outstr = callback(conn, protolist)
+
+ # Save our callback arguments on the connection object. This is
+ # done to make sure that they don't get freed before OpenSSL
+ # uses them. Then, return them appropriately in the output
+ # parameters.
+ conn._npn_select_callback_args = [
+ _ffi.new("unsigned char *", len(outstr)),
+ _ffi.new("unsigned char[]", outstr),
+ ]
+ outlen[0] = conn._npn_select_callback_args[0][0]
+ out[0] = conn._npn_select_callback_args[1]
+ return 0
+ except Exception as e:
+ self._problems.append(e)
+ return 2 # SSL_TLSEXT_ERR_ALERT_FATAL
+
+ self.callback = _ffi.callback(
+ ("int (*)(SSL *, unsigned char **, unsigned char *, "
+ "const unsigned char *, unsigned int, void *)"),
+ wrapper
+ )
+
+
+class _ALPNSelectHelper(_CallbackExceptionHelper):
+ """
+ Wrap a callback such that it can be used as an ALPN selection callback.
+ """
+
+ def __init__(self, callback):
+ _CallbackExceptionHelper.__init__(self)
+
+ @wraps(callback)
+ def wrapper(ssl, out, outlen, in_, inlen, arg):
+ try:
+ conn = Connection._reverse_mapping[ssl]
+
+ # The string passed to us is made up of multiple
+ # length-prefixed bytestrings. We need to split that into a
+ # list.
+ instr = _ffi.buffer(in_, inlen)[:]
+ protolist = []
+ while instr:
+ encoded_len = indexbytes(instr, 0)
+ proto = instr[1:encoded_len + 1]
+ protolist.append(proto)
+ instr = instr[encoded_len + 1:]
+
+ # Call the callback
+ outstr = callback(conn, protolist)
+
+ if not isinstance(outstr, _binary_type):
+ raise TypeError("ALPN callback must return a bytestring.")
+
+ # Save our callback arguments on the connection object to make
+ # sure that they don't get freed before OpenSSL can use them.
+ # Then, return them in the appropriate output parameters.
+ conn._alpn_select_callback_args = [
+ _ffi.new("unsigned char *", len(outstr)),
+ _ffi.new("unsigned char[]", outstr),
+ ]
+ outlen[0] = conn._alpn_select_callback_args[0][0]
+ out[0] = conn._alpn_select_callback_args[1]
+ return 0
+ except Exception as e:
+ self._problems.append(e)
+ return 2 # SSL_TLSEXT_ERR_ALERT_FATAL
+
+ self.callback = _ffi.callback(
+ ("int (*)(SSL *, unsigned char **, unsigned char *, "
+ "const unsigned char *, unsigned int, void *)"),
+ wrapper
+ )
+
+
+class _OCSPServerCallbackHelper(_CallbackExceptionHelper):
+ """
+ Wrap a callback such that it can be used as an OCSP callback for the server
+ side.
+
+ Annoyingly, OpenSSL defines one OCSP callback but uses it in two different
+ ways. For servers, that callback is expected to retrieve some OCSP data and
+ hand it to OpenSSL, and may return only SSL_TLSEXT_ERR_OK,
+ SSL_TLSEXT_ERR_FATAL, and SSL_TLSEXT_ERR_NOACK. For clients, that callback
+ is expected to check the OCSP data, and returns a negative value on error,
+ 0 if the response is not acceptable, or positive if it is. These are
+ mutually exclusive return code behaviours, and they mean that we need two
+ helpers so that we always return an appropriate error code if the user's
+ code throws an exception.
+
+ Given that we have to have two helpers anyway, these helpers are a bit more
+ helpery than most: specifically, they hide a few more of the OpenSSL
+ functions so that the user has an easier time writing these callbacks.
+
+ This helper implements the server side.
+ """
+
+ def __init__(self, callback):
+ _CallbackExceptionHelper.__init__(self)
+
+ @wraps(callback)
+ def wrapper(ssl, cdata):
+ try:
+ conn = Connection._reverse_mapping[ssl]
+
+ # Extract the data if any was provided.
+ if cdata != _ffi.NULL:
+ data = _ffi.from_handle(cdata)
+ else:
+ data = None
+
+ # Call the callback.
+ ocsp_data = callback(conn, data)
+
+ if not isinstance(ocsp_data, _binary_type):
+ raise TypeError("OCSP callback must return a bytestring.")
+
+ # If the OCSP data was provided, we will pass it to OpenSSL.
+ # However, we have an early exit here: if no OCSP data was
+ # provided we will just exit out and tell OpenSSL that there
+ # is nothing to do.
+ if not ocsp_data:
+ return 3 # SSL_TLSEXT_ERR_NOACK
+
+ # OpenSSL takes ownership of this data and expects it to have
+ # been allocated by OPENSSL_malloc.
+ ocsp_data_length = len(ocsp_data)
+ data_ptr = _lib.OPENSSL_malloc(ocsp_data_length)
+ _ffi.buffer(data_ptr, ocsp_data_length)[:] = ocsp_data
+
+ _lib.SSL_set_tlsext_status_ocsp_resp(
+ ssl, data_ptr, ocsp_data_length
+ )
+
+ return 0
+ except Exception as e:
+ self._problems.append(e)
+ return 2 # SSL_TLSEXT_ERR_ALERT_FATAL
+
+ self.callback = _ffi.callback("int (*)(SSL *, void *)", wrapper)
+
+
+class _OCSPClientCallbackHelper(_CallbackExceptionHelper):
+ """
+ Wrap a callback such that it can be used as an OCSP callback for the client
+ side.
+
+ Annoyingly, OpenSSL defines one OCSP callback but uses it in two different
+ ways. For servers, that callback is expected to retrieve some OCSP data and
+ hand it to OpenSSL, and may return only SSL_TLSEXT_ERR_OK,
+ SSL_TLSEXT_ERR_FATAL, and SSL_TLSEXT_ERR_NOACK. For clients, that callback
+ is expected to check the OCSP data, and returns a negative value on error,
+ 0 if the response is not acceptable, or positive if it is. These are
+ mutually exclusive return code behaviours, and they mean that we need two
+ helpers so that we always return an appropriate error code if the user's
+ code throws an exception.
+
+ Given that we have to have two helpers anyway, these helpers are a bit more
+ helpery than most: specifically, they hide a few more of the OpenSSL
+ functions so that the user has an easier time writing these callbacks.
+
+ This helper implements the client side.
+ """
+
+ def __init__(self, callback):
+ _CallbackExceptionHelper.__init__(self)
+
+ @wraps(callback)
+ def wrapper(ssl, cdata):
+ try:
+ conn = Connection._reverse_mapping[ssl]
+
+ # Extract the data if any was provided.
+ if cdata != _ffi.NULL:
+ data = _ffi.from_handle(cdata)
+ else:
+ data = None
+
+ # Get the OCSP data.
+ ocsp_ptr = _ffi.new("unsigned char **")
+ ocsp_len = _lib.SSL_get_tlsext_status_ocsp_resp(ssl, ocsp_ptr)
+ if ocsp_len < 0:
+ # No OCSP data.
+ ocsp_data = b''
+ else:
+ # Copy the OCSP data, then pass it to the callback.
+ ocsp_data = _ffi.buffer(ocsp_ptr[0], ocsp_len)[:]
+
+ valid = callback(conn, ocsp_data, data)
+
+ # Return 1 on success or 0 on error.
+ return int(bool(valid))
+
+ except Exception as e:
+ self._problems.append(e)
+ # Return negative value if an exception is hit.
+ return -1
+
+ self.callback = _ffi.callback("int (*)(SSL *, void *)", wrapper)
+
+
+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 :const:`SSLEAY_` constants defined in this module.
+ """
+ return _ffi.string(_lib.SSLeay_version(type))
+
+
+def _make_requires(flag, error):
+ """
+ Builds a decorator that ensures that functions that rely on OpenSSL
+ functions that are not present in this build raise NotImplementedError,
+ rather than AttributeError coming out of cryptography.
+
+ :param flag: A cryptography flag that guards the functions, e.g.
+ ``Cryptography_HAS_NEXTPROTONEG``.
+ :param error: The string to be used in the exception if the flag is false.
+ """
+ def _requires_decorator(func):
+ if not flag:
+ @wraps(func)
+ def explode(*args, **kwargs):
+ raise NotImplementedError(error)
+ return explode
+ else:
+ return func
+
+ return _requires_decorator
+
+
+_requires_npn = _make_requires(
+ _lib.Cryptography_HAS_NEXTPROTONEG, "NPN not available"
+)
+
+
+_requires_alpn = _make_requires(
+ _lib.Cryptography_HAS_ALPN, "ALPN not available"
+)
+
+
+_requires_sni = _make_requires(
+ _lib.Cryptography_HAS_TLSEXT_HOSTNAME, "SNI not available"
+)
+
+
+class Session(object):
+ """
+ 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
+ """
+ pass
+
+
+class Context(object):
+ """
+ :class:`OpenSSL.SSL.Context` instances define the parameters for setting
+ up new SSL connections.
+
+ :param method: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, or
+ TLSv1_METHOD.
+ """
+ _methods = {
+ SSLv2_METHOD: "SSLv2_method",
+ 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):
+ 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()
+ _openssl_assert(method_obj != _ffi.NULL)
+
+ context = _lib.SSL_CTX_new(method_obj)
+ _openssl_assert(context != _ffi.NULL)
+ context = _ffi.gc(context, _lib.SSL_CTX_free)
+
+ # If SSL_CTX_set_ecdh_auto is available then set it so the ECDH curve
+ # will be auto-selected. This function was added in 1.0.2 and made a
+ # noop in 1.1.0+ (where it is set automatically).
+ try:
+ res = _lib.SSL_CTX_set_ecdh_auto(context, 1)
+ _openssl_assert(res == 1)
+ except AttributeError:
+ pass
+
+ 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
+ self._npn_advertise_helper = None
+ self._npn_advertise_callback = None
+ self._npn_select_helper = None
+ self._npn_select_callback = None
+ self._alpn_select_helper = None
+ self._alpn_select_callback = None
+ self._ocsp_helper = None
+ self._ocsp_callback = None
+ self._ocsp_data = None
+
+ 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. 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 :data:`None`.
+
+ :param cafile: In which file we can find the certificates (``bytes`` or
+ ``unicode``).
+ :param capath: In which directory we can find the certificates
+ (``bytes`` or ``unicode``).
+
+ :return: None
+ """
+ if cafile is None:
+ cafile = _ffi.NULL
+ else:
+ cafile = _path_string(cafile)
+
+ if capath is None:
+ capath = _ffi.NULL
+ else:
+ capath = _path_string(capath)
+
+ 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. This function will be called
+ when a private key with a passphrase is loaded.
+
+ :param callback: The Python callback to use. This 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 :meth:`set_passwd_cb`. The *callback* must return
+ a byte string. If an error occurs, *callback* should return a false
+ value (e.g. an empty string).
+ :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):
+ """
+ Specify that the platform provided CA certificates are to be used for
+ verification purposes. This method has some caveats related to the
+ binary wheels that cryptography (pyOpenSSL's primary dependency) ships:
+
+ * macOS will only load certificates using this method if the user has
+ the ``openssl@1.1`` `Homebrew <https://brew.sh>`_ formula installed
+ in the default location.
+ * Windows will not work.
+ * manylinux1 cryptography wheels will work on most common Linux
+ distributions in pyOpenSSL 17.1.0 and above. pyOpenSSL detects the
+ manylinux1 wheel and attempts to load roots via a fallback path.
+
+ :return: None
+ """
+ # SSL_CTX_set_default_verify_paths will attempt to load certs from
+ # both a cafile and capath that are set at compile time. However,
+ # it will first check environment variables and, if present, load
+ # those paths instead
+ set_result = _lib.SSL_CTX_set_default_verify_paths(self._context)
+ _openssl_assert(set_result == 1)
+ # After attempting to set default_verify_paths we need to know whether
+ # to go down the fallback path.
+ # First we'll check to see if any env vars have been set. If so,
+ # we won't try to do anything else because the user has set the path
+ # themselves.
+ dir_env_var = _ffi.string(
+ _lib.X509_get_default_cert_dir_env()
+ ).decode("ascii")
+ file_env_var = _ffi.string(
+ _lib.X509_get_default_cert_file_env()
+ ).decode("ascii")
+ if not self._check_env_vars_set(dir_env_var, file_env_var):
+ default_dir = _ffi.string(_lib.X509_get_default_cert_dir())
+ default_file = _ffi.string(_lib.X509_get_default_cert_file())
+ # Now we check to see if the default_dir and default_file are set
+ # to the exact values we use in our manylinux1 builds. If they are
+ # then we know to load the fallbacks
+ if (
+ default_dir == _CRYPTOGRAPHY_MANYLINUX1_CA_DIR and
+ default_file == _CRYPTOGRAPHY_MANYLINUX1_CA_FILE
+ ):
+ # This is manylinux1, let's load our fallback paths
+ self._fallback_default_verify_paths(
+ _CERTIFICATE_FILE_LOCATIONS,
+ _CERTIFICATE_PATH_LOCATIONS
+ )
+
+ def _check_env_vars_set(self, dir_env_var, file_env_var):
+ """
+ Check to see if the default cert dir/file environment vars are present.
+
+ :return: bool
+ """
+ return (
+ os.environ.get(file_env_var) is not None or
+ os.environ.get(dir_env_var) is not None
+ )
+
+ def _fallback_default_verify_paths(self, file_path, dir_path):
+ """
+ Default verify paths are based on the compiled version of OpenSSL.
+ However, when pyca/cryptography is compiled as a manylinux1 wheel
+ that compiled location can potentially be wrong. So, like Go, we
+ will try a predefined set of paths and attempt to load roots
+ from there.
+
+ :return: None
+ """
+ for cafile in file_path:
+ if os.path.isfile(cafile):
+ self.load_verify_locations(cafile)
+ break
+
+ for capath in dir_path:
+ if os.path.isdir(capath):
+ self.load_verify_locations(None, capath)
+ break
+
+ def use_certificate_chain_file(self, certfile):
+ """
+ Load a certificate chain from a file.
+
+ :param certfile: The name of the certificate chain file (``bytes`` or
+ ``unicode``). Must be PEM encoded.
+
+ :return: None
+ """
+ certfile = _path_string(certfile)
+
+ 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 (``bytes`` or
+ ``unicode``).
+ :param filetype: (optional) The encoding of the file, which is either
+ :const:`FILETYPE_PEM` or :const:`FILETYPE_ASN1`. The default is
+ :const:`FILETYPE_PEM`.
+
+ :return: None
+ """
+ certfile = _path_string(certfile)
+ 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 not None:
+ self._passphrase_helper.raise_if_problem(Error)
+
+ _raise_current_error()
+
+ def use_privatekey_file(self, keyfile, filetype=_UNSPECIFIED):
+ """
+ Load a private key from a file
+
+ :param keyfile: The name of the key file (``bytes`` or ``unicode``)
+ :param filetype: (optional) The encoding of the file, which is either
+ :const:`FILETYPE_PEM` or :const:`FILETYPE_ASN1`. The default is
+ :const:`FILETYPE_PEM`.
+
+ :return: None
+ """
+ keyfile = _path_string(keyfile)
+
+ 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 if the private key (loaded with :meth:`use_privatekey`) matches
+ the certificate (loaded with :meth:`use_certificate`)
+
+ :return: :data:`None` (raises :exc:`Error` if something's wrong)
+ """
+ if not _lib.SSL_CTX_check_private_key(self._context):
+ _raise_current_error()
+
+ def load_client_ca(self, cafile):
+ """
+ Load the trusted certificates that will be sent to the client. Does
+ not actually imply any of the certificates are trusted; that must be
+ configured separately.
+
+ :param bytes cafile: The path to a certificates file in PEM format.
+ :return: None
+ """
+ ca_list = _lib.SSL_load_client_CA_file(
+ _text_to_bytes_and_warn("cafile", cafile)
+ )
+ _openssl_assert(ca_list != _ffi.NULL)
+ _lib.SSL_CTX_set_client_CA_list(self._context, ca_list)
+
+ def set_session_id(self, buf):
+ """
+ Set the session id to *buf* 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.
+
+ :param bytes buf: The session id.
+
+ :returns: None
+ """
+ buf = _text_to_bytes_and_warn("buf", buf)
+ _openssl_assert(
+ _lib.SSL_CTX_set_session_id_context(
+ self._context,
+ buf,
+ len(buf),
+ ) == 1
+ )
+
+ def set_session_cache_mode(self, mode):
+ """
+ Set the behavior of the session cache used by all connections using
+ this Context. The previously set mode is returned. See
+ :const:`SESS_CACHE_*` for details about particular modes.
+
+ :param mode: One or more of the SESS_CACHE_* flags (combine using
+ bitwise or)
+ :returns: The previously set caching mode.
+
+ .. versionadded:: 0.14
+ """
+ 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):
+ """
+ Get the current session cache mode.
+
+ :returns: The currently used cache mode.
+
+ .. versionadded:: 0.14
+ """
+ return _lib.SSL_CTX_get_session_cache_mode(self._context)
+
+ def set_verify(self, mode, callback):
+ """
+ et the verification flags for this Context object to *mode* and specify
+ that *callback* should be used for verification callbacks.
+
+ :param mode: The verify mode, this should be one of
+ :const:`VERIFY_NONE` and :const:`VERIFY_PEER`. If
+ :const:`VERIFY_PEER` is used, *mode* can be OR:ed with
+ :const:`VERIFY_FAIL_IF_NO_PEER_CERT` and
+ :const:`VERIFY_CLIENT_ONCE` to further control the behaviour.
+ :param callback: The Python callback to use. This 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.
+ :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(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 maximum depth for the certificate chain verification that shall
+ be allowed for this Context object.
+
+ :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):
+ """
+ Retrieve the Context object's verify mode, as set by
+ :meth:`set_verify`.
+
+ :return: The verify mode
+ """
+ return _lib.SSL_CTX_get_verify_mode(self._context)
+
+ def get_verify_depth(self):
+ """
+ Retrieve the Context object's verify depth, as set by
+ :meth:`set_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 (``bytes`` or
+ ``unicode``).
+
+ :return: None
+ """
+ dhfile = _path_string(dhfile)
+
+ 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_tmp_ecdh(self, curve):
+ """
+ Select a curve to use for ECDHE key exchange.
+
+ :param curve: A curve object to use as returned by either
+ :meth:`OpenSSL.crypto.get_elliptic_curve` or
+ :meth:`OpenSSL.crypto.get_elliptic_curves`.
+
+ :return: None
+ """
+ _lib.SSL_CTX_set_tmp_ecdh(self._context, curve._to_EC_KEY())
+
+ def set_cipher_list(self, cipher_list):
+ """
+ Set the list of ciphers to be used in this context.
+
+ See the OpenSSL manual for more information (e.g.
+ :manpage:`ciphers(1)`).
+
+ :param bytes cipher_list: An OpenSSL cipher string.
+ :return: None
+ """
+ cipher_list = _text_to_bytes_and_warn("cipher_list", cipher_list)
+
+ if not isinstance(cipher_list, bytes):
+ raise TypeError("cipher_list must be a byte string.")
+
+ _openssl_assert(
+ _lib.SSL_CTX_set_cipher_list(self._context, cipher_list) == 1
+ )
+ # In OpenSSL 1.1.1 setting the cipher list will always return TLS 1.3
+ # ciphers even if you pass an invalid cipher. Applications (like
+ # Twisted) have tests that depend on an error being raised if an
+ # invalid cipher string is passed, but without the following check
+ # for the TLS 1.3 specific cipher suites it would never error.
+ tmpconn = Connection(self, None)
+ _openssl_assert(
+ tmpconn.get_cipher_list() != [
+ 'TLS_AES_256_GCM_SHA384',
+ 'TLS_CHACHA20_POLY1305_SHA256',
+ 'TLS_AES_128_GCM_SHA256'
+ ]
+ )
+
+ 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
+
+ .. versionadded:: 0.10
+ """
+ name_stack = _lib.sk_X509_NAME_new_null()
+ _openssl_assert(name_stack != _ffi.NULL)
+
+ 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)
+ _openssl_assert(copy != _ffi.NULL)
+ push_result = _lib.sk_X509_NAME_push(name_stack, copy)
+ if not push_result:
+ _lib.X509_NAME_free(copy)
+ _raise_current_error()
+ except Exception:
+ _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
+
+ .. versionadded:: 0.10
+ """
+ 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)
+ _openssl_assert(add_result == 1)
+
+ def set_timeout(self, timeout):
+ """
+ Set the timeout for newly created sessions for this Context object to
+ *timeout*. The default value is 300 seconds. See the OpenSSL manual
+ for more information (e.g. :manpage:`SSL_CTX_set_timeout(3)`).
+
+ :param timeout: The timeout in (whole) 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):
+ """
+ Retrieve session timeout, as set by :meth:`set_timeout`. The default
+ is 300 seconds.
+
+ :return: The session timeout
+ """
+ return _lib.SSL_CTX_get_timeout(self._context)
+
+ def set_info_callback(self, callback):
+ """
+ Set the information callback to *callback*. This function will be
+ called from time to time during SSL handshakes.
+
+ :param callback: The Python callback to use. This 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.
+ :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 :meth:`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. This can be used to add
+ "trusted" certificates without using the
+ :meth:`load_verify_locations` method.
+
+ :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!
+ This method should be used with the :const:`OP_*` constants.
+
+ :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! This method
+ should be used with the :const:`MODE_*` constants.
+
+ :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)
+
+ @_requires_sni
+ 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.
+
+ .. versionadded:: 0.13
+ """
+ @wraps(callback)
+ def wrapper(ssl, alert, arg):
+ callback(Connection._reverse_mapping[ssl])
+ return 0
+
+ self._tlsext_servername_callback = _ffi.callback(
+ "int (*)(SSL *, int *, void *)", wrapper)
+ _lib.SSL_CTX_set_tlsext_servername_callback(
+ self._context, self._tlsext_servername_callback)
+
+ def set_tlsext_use_srtp(self, profiles):
+ """
+ Enable support for negotiating SRTP keying material.
+
+ :param bytes profiles: A colon delimited list of protection profile
+ names, like ``b'SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32'``.
+ :return: None
+ """
+ if not isinstance(profiles, bytes):
+ raise TypeError("profiles must be a byte string.")
+
+ _openssl_assert(
+ _lib.SSL_CTX_set_tlsext_use_srtp(self._context, profiles) == 0
+ )
+
+ @_requires_npn
+ def set_npn_advertise_callback(self, callback):
+ """
+ Specify a callback function that will be called when offering `Next
+ Protocol Negotiation
+ <https://technotes.googlecode.com/git/nextprotoneg.html>`_ as a server.
+
+ :param callback: The callback function. It will be invoked with one
+ argument, the :class:`Connection` instance. It should return a
+ list of bytestrings representing the advertised protocols, like
+ ``[b'http/1.1', b'spdy/2']``.
+
+ .. versionadded:: 0.15
+ """
+ self._npn_advertise_helper = _NpnAdvertiseHelper(callback)
+ self._npn_advertise_callback = self._npn_advertise_helper.callback
+ _lib.SSL_CTX_set_next_protos_advertised_cb(
+ self._context, self._npn_advertise_callback, _ffi.NULL)
+
+ @_requires_npn
+ def set_npn_select_callback(self, callback):
+ """
+ Specify a callback function that will be called when a server offers
+ Next Protocol Negotiation options.
+
+ :param callback: The callback function. It will be invoked with two
+ arguments: the Connection, and a list of offered protocols as
+ bytestrings, e.g. ``[b'http/1.1', b'spdy/2']``. It should return
+ one of those bytestrings, the chosen protocol.
+
+ .. versionadded:: 0.15
+ """
+ self._npn_select_helper = _NpnSelectHelper(callback)
+ self._npn_select_callback = self._npn_select_helper.callback
+ _lib.SSL_CTX_set_next_proto_select_cb(
+ self._context, self._npn_select_callback, _ffi.NULL)
+
+ @_requires_alpn
+ def set_alpn_protos(self, protos):
+ """
+ Specify the protocols that the client is prepared to speak after the
+ TLS connection has been negotiated using Application Layer Protocol
+ Negotiation.
+
+ :param protos: A list of the protocols to be offered to the server.
+ This list should be a Python list of bytestrings representing the
+ protocols to offer, e.g. ``[b'http/1.1', b'spdy/2']``.
+ """
+ # Take the list of protocols and join them together, prefixing them
+ # with their lengths.
+ protostr = b''.join(
+ chain.from_iterable((int2byte(len(p)), p) for p in protos)
+ )
+
+ # Build a C string from the list. We don't need to save this off
+ # because OpenSSL immediately copies the data out.
+ input_str = _ffi.new("unsigned char[]", protostr)
+ _lib.SSL_CTX_set_alpn_protos(self._context, input_str, len(protostr))
+
+ @_requires_alpn
+ def set_alpn_select_callback(self, callback):
+ """
+ Specify a callback function that will be called on the server when a
+ client offers protocols using ALPN.
+
+ :param callback: The callback function. It will be invoked with two
+ arguments: the Connection, and a list of offered protocols as
+ bytestrings, e.g ``[b'http/1.1', b'spdy/2']``. It should return
+ one of those bytestrings, the chosen protocol.
+ """
+ self._alpn_select_helper = _ALPNSelectHelper(callback)
+ self._alpn_select_callback = self._alpn_select_helper.callback
+ _lib.SSL_CTX_set_alpn_select_cb(
+ self._context, self._alpn_select_callback, _ffi.NULL)
+
+ def _set_ocsp_callback(self, helper, data):
+ """
+ This internal helper does the common work for
+ ``set_ocsp_server_callback`` and ``set_ocsp_client_callback``, which is
+ almost all of it.
+ """
+ self._ocsp_helper = helper
+ self._ocsp_callback = helper.callback
+ if data is None:
+ self._ocsp_data = _ffi.NULL
+ else:
+ self._ocsp_data = _ffi.new_handle(data)
+
+ rc = _lib.SSL_CTX_set_tlsext_status_cb(
+ self._context, self._ocsp_callback
+ )
+ _openssl_assert(rc == 1)
+ rc = _lib.SSL_CTX_set_tlsext_status_arg(self._context, self._ocsp_data)
+ _openssl_assert(rc == 1)
+
+ def set_ocsp_server_callback(self, callback, data=None):
+ """
+ Set a callback to provide OCSP data to be stapled to the TLS handshake
+ on the server side.
+
+ :param callback: The callback function. It will be invoked with two
+ arguments: the Connection, and the optional arbitrary data you have
+ provided. The callback must return a bytestring that contains the
+ OCSP data to staple to the handshake. If no OCSP data is available
+ for this connection, return the empty bytestring.
+ :param data: Some opaque data that will be passed into the callback
+ function when called. This can be used to avoid needing to do
+ complex data lookups or to keep track of what context is being
+ used. This parameter is optional.
+ """
+ helper = _OCSPServerCallbackHelper(callback)
+ self._set_ocsp_callback(helper, data)
+
+ def set_ocsp_client_callback(self, callback, data=None):
+ """
+ Set a callback to validate OCSP data stapled to the TLS handshake on
+ the client side.
+
+ :param callback: The callback function. It will be invoked with three
+ arguments: the Connection, a bytestring containing the stapled OCSP
+ assertion, and the optional arbitrary data you have provided. The
+ callback must return a boolean that indicates the result of
+ validating the OCSP data: ``True`` if the OCSP data is valid and
+ the certificate can be trusted, or ``False`` if either the OCSP
+ data is invalid or the certificate has been revoked.
+ :param data: Some opaque data that will be passed into the callback
+ function when called. This can be used to avoid needing to do
+ complex data lookups or to keep track of what context is being
+ used. This parameter is optional.
+ """
+ helper = _OCSPClientCallbackHelper(callback)
+ self._set_ocsp_callback(helper, data)
+
+
+ContextType = deprecated(
+ Context, __name__,
+ "ContextType has been deprecated, use Context instead", DeprecationWarning
+)
+
+
+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)
+ # We set SSL_MODE_AUTO_RETRY to handle situations where OpenSSL returns
+ # an SSL_ERROR_WANT_READ when processing a non-application data packet
+ # even though there is still data on the underlying transport.
+ # See https://github.com/openssl/openssl/issues/6234 for more details.
+ _lib.SSL_set_mode(self._ssl, _lib.SSL_MODE_AUTO_RETRY)
+ self._context = context
+ self._app_data = None
+
+ # References to strings used for Next Protocol Negotiation. OpenSSL's
+ # header files suggest that these might get copied at some point, but
+ # doesn't specify when, so we store them here to make sure they don't
+ # get freed before OpenSSL uses them.
+ self._npn_advertise_callback_args = None
+ self._npn_select_callback_args = None
+
+ # References to strings used for Application Layer Protocol
+ # Negotiation. These strings get copied at some point but it's well
+ # after the callback returns, so we have to hang them somewhere to
+ # avoid them getting freed.
+ self._alpn_select_callback_args = None
+
+ 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())
+ _openssl_assert(self._into_ssl != _ffi.NULL)
+
+ self._from_ssl = _lib.BIO_new(_lib.BIO_s_mem())
+ _openssl_assert(self._from_ssl != _ffi.NULL)
+
+ _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))
+ _openssl_assert(set_result == 1)
+
+ def __getattr__(self, name):
+ """
+ Look up attributes on the wrapped socket object if they are not found
+ on the Connection object.
+ """
+ if self._socket is None:
+ raise AttributeError("'%s' object has no attribute '%s'" % (
+ self.__class__.__name__, name
+ ))
+ else:
+ 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()
+ if self._context._npn_advertise_helper is not None:
+ self._context._npn_advertise_helper.raise_if_problem()
+ if self._context._npn_select_helper is not None:
+ self._context._npn_select_helper.raise_if_problem()
+ if self._context._alpn_select_helper is not None:
+ self._context._alpn_select_helper.raise_if_problem()
+ if self._context._ocsp_helper is not None:
+ self._context._ocsp_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
+
+ if errno != 0:
+ raise SysCallError(errno, errorcode.get(errno))
+ 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):
+ """
+ Retrieve the :class:`Context` object associated with this
+ :class:`Connection`.
+ """
+ return self._context
+
+ def set_context(self, context):
+ """
+ Switch this connection to a new session context.
+
+ :param context: A :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
+
+ @_requires_sni
+ 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 :data:`None`.
+
+ .. versionadded:: 0.13
+ """
+ name = _lib.SSL_get_servername(
+ self._ssl, _lib.TLSEXT_NAMETYPE_host_name
+ )
+ if name == _ffi.NULL:
+ return None
+
+ return _ffi.string(name)
+
+ @_requires_sni
+ 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.
+
+ .. versionadded:: 0.13
+ """
+ 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 SSL buffer
+ (**not** the underlying transport buffer).
+
+ :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, buffer or memoryview to send
+ :param flags: (optional) Included for compatibility with the socket
+ API, the value is ignored
+ :return: The number of bytes written
+ """
+ # Backward compatibility
+ buf = _text_to_bytes_and_warn("buf", buf)
+
+ if isinstance(buf, memoryview):
+ buf = buf.tobytes()
+ if isinstance(buf, _buffer):
+ buf = str(buf)
+ if not isinstance(buf, bytes):
+ raise TypeError("data must be a memoryview, buffer or byte string")
+ if len(buf) > 2147483647:
+ raise ValueError("Cannot send more than 2**31-1 bytes at once.")
+
+ 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, buffer or memoryview to send
+ :param flags: (optional) Included for compatibility with the socket
+ API, the value is ignored
+ :return: The number of bytes written
+ """
+ buf = _text_to_bytes_and_warn("buf", buf)
+
+ if isinstance(buf, memoryview):
+ buf = buf.tobytes()
+ if isinstance(buf, _buffer):
+ buf = str(buf)
+ if not isinstance(buf, bytes):
+ raise TypeError("buf must be a memoryview, buffer or byte string")
+
+ left_to_send = len(buf)
+ total_sent = 0
+ data = _ffi.new("char[]", buf)
+
+ while left_to_send:
+ # SSL_write's num arg is an int,
+ # so we cannot send more than 2**31-1 bytes at once.
+ result = _lib.SSL_write(
+ self._ssl,
+ data + total_sent,
+ min(left_to_send, 2147483647)
+ )
+ 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.
+
+ :param bufsiz: The maximum number of bytes to read
+ :param flags: (optional) The only supported flag is ``MSG_PEEK``,
+ all other flags are ignored.
+ :return: The string read from the Connection
+ """
+ buf = _no_zero_allocator("char[]", bufsiz)
+ if flags is not None and flags & socket.MSG_PEEK:
+ result = _lib.SSL_peek(self._ssl, buf, bufsiz)
+ else:
+ result = _lib.SSL_read(self._ssl, buf, bufsiz)
+ self._raise_ssl_error(self._ssl, result)
+ return _ffi.buffer(buf, result)[:]
+ read = recv
+
+ def recv_into(self, buffer, nbytes=None, flags=None):
+ """
+ Receive data on the connection and copy it directly into the provided
+ buffer, rather than creating a new string.
+
+ :param buffer: The buffer to copy into.
+ :param nbytes: (optional) The maximum number of bytes to read into the
+ buffer. If not present, defaults to the size of the buffer. If
+ larger than the size of the buffer, is reduced to the size of the
+ buffer.
+ :param flags: (optional) The only supported flag is ``MSG_PEEK``,
+ all other flags are ignored.
+ :return: The number of bytes read into the buffer.
+ """
+ if nbytes is None:
+ nbytes = len(buffer)
+ else:
+ nbytes = min(nbytes, len(buffer))
+
+ # We need to create a temporary buffer. This is annoying, it would be
+ # better if we could pass memoryviews straight into the SSL_read call,
+ # but right now we can't. Revisit this if CFFI gets that ability.
+ buf = _no_zero_allocator("char[]", nbytes)
+ if flags is not None and flags & socket.MSG_PEEK:
+ result = _lib.SSL_peek(self._ssl, buf, nbytes)
+ else:
+ result = _lib.SSL_read(self._ssl, buf, nbytes)
+ self._raise_ssl_error(self._ssl, result)
+
+ # This strange line is all to avoid a memory copy. The buffer protocol
+ # should allow us to assign a CFFI buffer to the LHS of this line, but
+ # on CPython 3.3+ that segfaults. As a workaround, we can temporarily
+ # wrap it in a memoryview.
+ buffer[:result] = memoryview(_ffi.buffer(buf, result))
+
+ return result
+
+ 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):
+ """
+ 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.
+
+ :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 = _no_zero_allocator("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):
+ """
+ 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
+ :meth:`recv`).
+
+ :param buf: The string to put into the memory BIO.
+ :return: The number of bytes written
+ """
+ buf = _text_to_bytes_and_warn("buf", buf)
+
+ if self._into_ssl is None:
+ raise TypeError("Connection sock was not None")
+
+ 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
+ :rtype: bool
+ """
+ if not self.renegotiate_pending():
+ _openssl_assert(_lib.SSL_renegotiate(self._ssl) == 1)
+ return True
+ return False
+
+ def do_handshake(self):
+ """
+ Perform an SSL handshake (usually called after :meth:`renegotiate` or
+ one of :meth:`set_accept_state` or :meth:`set_accept_state`). This can
+ raise the same exceptions as :meth:`send` and :meth:`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
+ :rtype: bool
+ """
+ return _lib.SSL_renegotiate_pending(self._ssl) == 1
+
+ def total_renegotiations(self):
+ """
+ Find out the total number of renegotiations.
+
+ :return: The number of renegotiations.
+ :rtype: int
+ """
+ return _lib.SSL_total_renegotiations(self._ssl)
+
+ def connect(self, addr):
+ """
+ Call the :meth:`connect` method of the underlying socket and set up SSL
+ on the socket, using the :class:`Context` object supplied to this
+ :class:`Connection` object at creation.
+
+ :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):
+ """
+ Call the :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 :meth:`connect_ex` method of the
+ socket 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):
+ """
+ Call the :meth:`accept` method of the underlying socket and set up SSL
+ on the returned socket, using the Context object supplied to this
+ :class:`Connection` object at creation.
+
+ :return: A *(conn, addr)* pair where *conn* is the new
+ :class:`Connection` object created, and *address* is as returned by
+ the socket's :meth:`accept`.
+ """
+ client, addr = self._socket.accept()
+ conn = Connection(self._context, client)
+ conn.set_accept_state()
+ return (conn, addr)
+
+ def bio_shutdown(self):
+ """
+ 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.
+
+ :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 the shutdown message to the Connection.
+
+ :return: True if the shutdown completed successfully (i.e. both sides
+ have sent closure alerts), False otherwise (in which case you
+ call :meth:`recv` or :meth:`send` when the connection becomes
+ readable/writeable).
+ """
+ result = _lib.SSL_shutdown(self._ssl)
+ if result < 0:
+ self._raise_ssl_error(self._ssl, result)
+ elif result > 0:
+ return True
+ else:
+ return False
+
+ def get_cipher_list(self):
+ """
+ Retrieve the list of ciphers used by the Connection object.
+
+ :return: A list of native 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, 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`.
+
+ If this is a client connection, the list will be empty until the
+ connection with the server is established.
+
+ .. versionadded:: 0.10
+ """
+ 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)
+ _openssl_assert(copy != _ffi.NULL)
+
+ pyname = X509Name.__new__(X509Name)
+ pyname._name = _ffi.gc(copy, _lib.X509_NAME_free)
+ result.append(pyname)
+ return result
+
+ def makefile(self, *args, **kwargs):
+ """
+ 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):
+ """
+ Retrieve application data as set by :meth:`set_app_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 the shutdown state of the Connection.
+
+ :return: The shutdown state, a bitvector of SENT_SHUTDOWN,
+ RECEIVED_SHUTDOWN.
+ """
+ return _lib.SSL_get_shutdown(self._ssl)
+
+ def set_shutdown(self, state):
+ """
+ Set the shutdown state of the Connection.
+
+ :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 get_state_string(self):
+ """
+ Retrieve a verbose string detailing the state of the Connection.
+
+ :return: A string representing the state
+ :rtype: bytes
+ """
+ return _ffi.string(_lib.SSL_state_string_long(self._ssl))
+
+ def server_random(self):
+ """
+ Retrieve the random value used with the server hello message.
+
+ :return: A string representing the state
+ """
+ session = _lib.SSL_get_session(self._ssl)
+ if session == _ffi.NULL:
+ return None
+ length = _lib.SSL_get_server_random(self._ssl, _ffi.NULL, 0)
+ assert length > 0
+ outp = _no_zero_allocator("unsigned char[]", length)
+ _lib.SSL_get_server_random(self._ssl, outp, length)
+ return _ffi.buffer(outp, length)[:]
+
+ def client_random(self):
+ """
+ Retrieve the random value used with the client hello message.
+
+ :return: A string representing the state
+ """
+ session = _lib.SSL_get_session(self._ssl)
+ if session == _ffi.NULL:
+ return None
+
+ length = _lib.SSL_get_client_random(self._ssl, _ffi.NULL, 0)
+ assert length > 0
+ outp = _no_zero_allocator("unsigned char[]", length)
+ _lib.SSL_get_client_random(self._ssl, outp, length)
+ return _ffi.buffer(outp, length)[:]
+
+ def master_key(self):
+ """
+ Retrieve the value of the master key for this session.
+
+ :return: A string representing the state
+ """
+ session = _lib.SSL_get_session(self._ssl)
+ if session == _ffi.NULL:
+ return None
+
+ length = _lib.SSL_SESSION_get_master_key(session, _ffi.NULL, 0)
+ assert length > 0
+ outp = _no_zero_allocator("unsigned char[]", length)
+ _lib.SSL_SESSION_get_master_key(session, outp, length)
+ return _ffi.buffer(outp, length)[:]
+
+ def export_keying_material(self, label, olen, context=None):
+ """
+ Obtain keying material for application use.
+
+ :param: label - a disambiguating label string as described in RFC 5705
+ :param: olen - the length of the exported key material in bytes
+ :param: context - a per-association context value
+ :return: the exported key material bytes or None
+ """
+ outp = _no_zero_allocator("unsigned char[]", olen)
+ context_buf = _ffi.NULL
+ context_len = 0
+ use_context = 0
+ if context is not None:
+ context_buf = context
+ context_len = len(context)
+ use_context = 1
+ success = _lib.SSL_export_keying_material(self._ssl, outp, olen,
+ label, len(label),
+ context_buf, context_len,
+ use_context)
+ _openssl_assert(success == 1)
+ return _ffi.buffer(outp, olen)[:]
+
+ def sock_shutdown(self, *args, **kwargs):
+ """
+ Call the :meth:`shutdown` method of the underlying socket.
+ See :manpage:`shutdown(2)`.
+
+ :return: What the socket's shutdown() method returns
+ """
+ return self._socket.shutdown(*args, **kwargs)
+
+ def get_certificate(self):
+ """
+ Retrieve the local certificate (if any)
+
+ :return: The local certificate
+ """
+ cert = _lib.SSL_get_certificate(self._ssl)
+ if cert != _ffi.NULL:
+ _lib.X509_up_ref(cert)
+ return X509._from_raw_x509_ptr(cert)
+ return None
+
+ 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:
+ return X509._from_raw_x509_ptr(cert)
+ 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._from_raw_x509_ptr(cert)
+ 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 :class:`OpenSSL.SSL.Session` or
+ :obj:`None` if no session exists.
+
+ .. versionadded:: 0.14
+ """
+ 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
+
+ .. versionadded:: 0.14
+ """
+ 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()
+
+ def _get_finished_message(self, function):
+ """
+ Helper to implement :meth:`get_finished` and
+ :meth:`get_peer_finished`.
+
+ :param function: Either :data:`SSL_get_finished`: or
+ :data:`SSL_get_peer_finished`.
+
+ :return: :data:`None` if the desired message has not yet been
+ received, otherwise the contents of the message.
+ :rtype: :class:`bytes` or :class:`NoneType`
+ """
+ # The OpenSSL documentation says nothing about what might happen if the
+ # count argument given is zero. Specifically, it doesn't say whether
+ # the output buffer may be NULL in that case or not. Inspection of the
+ # implementation reveals that it calls memcpy() unconditionally.
+ # Section 7.1.4, paragraph 1 of the C standard suggests that
+ # memcpy(NULL, source, 0) is not guaranteed to produce defined (let
+ # alone desirable) behavior (though it probably does on just about
+ # every implementation...)
+ #
+ # Allocate a tiny buffer to pass in (instead of just passing NULL as
+ # one might expect) for the initial call so as to be safe against this
+ # potentially undefined behavior.
+ empty = _ffi.new("char[]", 0)
+ size = function(self._ssl, empty, 0)
+ if size == 0:
+ # No Finished message so far.
+ return None
+
+ buf = _no_zero_allocator("char[]", size)
+ function(self._ssl, buf, size)
+ return _ffi.buffer(buf, size)[:]
+
+ def get_finished(self):
+ """
+ Obtain the latest TLS Finished message that we sent.
+
+ :return: The contents of the message or :obj:`None` if the TLS
+ handshake has not yet completed.
+ :rtype: :class:`bytes` or :class:`NoneType`
+
+ .. versionadded:: 0.15
+ """
+ return self._get_finished_message(_lib.SSL_get_finished)
+
+ def get_peer_finished(self):
+ """
+ Obtain the latest TLS Finished message that we received from the peer.
+
+ :return: The contents of the message or :obj:`None` if the TLS
+ handshake has not yet completed.
+ :rtype: :class:`bytes` or :class:`NoneType`
+
+ .. versionadded:: 0.15
+ """
+ return self._get_finished_message(_lib.SSL_get_peer_finished)
+
+ def get_cipher_name(self):
+ """
+ Obtain the name of the currently used cipher.
+
+ :returns: The name of the currently used cipher or :obj:`None`
+ if no connection has been established.
+ :rtype: :class:`unicode` or :class:`NoneType`
+
+ .. versionadded:: 0.15
+ """
+ cipher = _lib.SSL_get_current_cipher(self._ssl)
+ if cipher == _ffi.NULL:
+ return None
+ else:
+ name = _ffi.string(_lib.SSL_CIPHER_get_name(cipher))
+ return name.decode("utf-8")
+
+ def get_cipher_bits(self):
+ """
+ Obtain the number of secret bits of the currently used cipher.
+
+ :returns: The number of secret bits of the currently used cipher
+ or :obj:`None` if no connection has been established.
+ :rtype: :class:`int` or :class:`NoneType`
+
+ .. versionadded:: 0.15
+ """
+ cipher = _lib.SSL_get_current_cipher(self._ssl)
+ if cipher == _ffi.NULL:
+ return None
+ else:
+ return _lib.SSL_CIPHER_get_bits(cipher, _ffi.NULL)
+
+ def get_cipher_version(self):
+ """
+ Obtain the protocol version of the currently used cipher.
+
+ :returns: The protocol name of the currently used cipher
+ or :obj:`None` if no connection has been established.
+ :rtype: :class:`unicode` or :class:`NoneType`
+
+ .. versionadded:: 0.15
+ """
+ cipher = _lib.SSL_get_current_cipher(self._ssl)
+ if cipher == _ffi.NULL:
+ return None
+ else:
+ version = _ffi.string(_lib.SSL_CIPHER_get_version(cipher))
+ return version.decode("utf-8")
+
+ def get_protocol_version_name(self):
+ """
+ Retrieve the protocol version of the current connection.
+
+ :returns: The TLS version of the current connection, for example
+ the value for TLS 1.2 would be ``TLSv1.2``or ``Unknown``
+ for connections that were not successfully established.
+ :rtype: :class:`unicode`
+ """
+ version = _ffi.string(_lib.SSL_get_version(self._ssl))
+ return version.decode("utf-8")
+
+ def get_protocol_version(self):
+ """
+ Retrieve the SSL or TLS protocol version of the current connection.
+
+ :returns: The TLS version of the current connection. For example,
+ it will return ``0x769`` for connections made over TLS version 1.
+ :rtype: :class:`int`
+ """
+ version = _lib.SSL_version(self._ssl)
+ return version
+
+ @_requires_npn
+ def get_next_proto_negotiated(self):
+ """
+ Get the protocol that was negotiated by NPN.
+
+ :returns: A bytestring of the protocol name. If no protocol has been
+ negotiated yet, returns an empty string.
+
+ .. versionadded:: 0.15
+ """
+ data = _ffi.new("unsigned char **")
+ data_len = _ffi.new("unsigned int *")
+
+ _lib.SSL_get0_next_proto_negotiated(self._ssl, data, data_len)
+
+ return _ffi.buffer(data[0], data_len[0])[:]
+
+ @_requires_alpn
+ def set_alpn_protos(self, protos):
+ """
+ Specify the client's ALPN protocol list.
+
+ These protocols are offered to the server during protocol negotiation.
+
+ :param protos: A list of the protocols to be offered to the server.
+ This list should be a Python list of bytestrings representing the
+ protocols to offer, e.g. ``[b'http/1.1', b'spdy/2']``.
+ """
+ # Take the list of protocols and join them together, prefixing them
+ # with their lengths.
+ protostr = b''.join(
+ chain.from_iterable((int2byte(len(p)), p) for p in protos)
+ )
+
+ # Build a C string from the list. We don't need to save this off
+ # because OpenSSL immediately copies the data out.
+ input_str = _ffi.new("unsigned char[]", protostr)
+ _lib.SSL_set_alpn_protos(self._ssl, input_str, len(protostr))
+
+ @_requires_alpn
+ def get_alpn_proto_negotiated(self):
+ """
+ Get the protocol that was negotiated by ALPN.
+
+ :returns: A bytestring of the protocol name. If no protocol has been
+ negotiated yet, returns an empty string.
+ """
+ data = _ffi.new("unsigned char **")
+ data_len = _ffi.new("unsigned int *")
+
+ _lib.SSL_get0_alpn_selected(self._ssl, data, data_len)
+
+ if not data_len:
+ return b''
+
+ return _ffi.buffer(data[0], data_len[0])[:]
+
+ def request_ocsp(self):
+ """
+ Called to request that the server sends stapled OCSP data, if
+ available. If this is not called on the client side then the server
+ will not send OCSP data. Should be used in conjunction with
+ :meth:`Context.set_ocsp_client_callback`.
+ """
+ rc = _lib.SSL_set_tlsext_status_type(
+ self._ssl, _lib.TLSEXT_STATUSTYPE_ocsp
+ )
+ _openssl_assert(rc == 1)
+
+
+ConnectionType = deprecated(
+ Connection, __name__,
+ "ConnectionType has been deprecated, use Connection instead",
+ DeprecationWarning
+)
+
+# 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()
--- /dev/null
+# Copyright (C) AB Strakt
+# See LICENSE for details.
+
+"""
+pyOpenSSL - A simple wrapper around the OpenSSL library
+"""
+
+from OpenSSL import crypto, SSL
+from OpenSSL.version import (
+ __author__, __copyright__, __email__, __license__, __summary__, __title__,
+ __uri__, __version__,
+)
+
+
+__all__ = [
+ "SSL", "crypto",
+
+ "__author__", "__copyright__", "__email__", "__license__", "__summary__",
+ "__title__", "__uri__", "__version__",
+]
--- /dev/null
+import sys
+import warnings
+
+from six import PY3, binary_type, text_type
+
+from cryptography.hazmat.bindings.openssl.binding import Binding
+
+
+binding = Binding()
+binding.init_static_locks()
+ffi = binding.ffi
+lib = binding.lib
+
+
+# This is a special CFFI allocator that does not bother to zero its memory
+# after allocation. This has vastly better performance on large allocations and
+# so should be used whenever we don't need the memory zeroed out.
+no_zero_allocator = ffi.new_allocator(should_clear_after_alloc=False)
+
+
+def text(charp):
+ """
+ Get a native string type representing of the given CFFI ``char*`` object.
+
+ :param charp: A C-style string represented using CFFI.
+
+ :return: :class:`str`
+ """
+ if not charp:
+ return ""
+ return native(ffi.string(charp))
+
+
+def exception_from_error_queue(exception_type):
+ """
+ Convert an OpenSSL library failure into a Python exception.
+
+ When a call to the native OpenSSL library fails, this is usually signalled
+ by the return value, and an error code is stored in an error queue
+ associated with the current thread. The err library provides functions to
+ obtain these error codes and textual error messages.
+ """
+ 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 exception_type(errors)
+
+
+def make_assert(error):
+ """
+ Create an assert function that uses :func:`exception_from_error_queue` to
+ raise an exception wrapped by *error*.
+ """
+ def openssl_assert(ok):
+ """
+ If *ok* is not True, retrieve the error from OpenSSL and raise it.
+ """
+ if ok is not True:
+ exception_from_error_queue(error)
+
+ return openssl_assert
+
+
+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
+
+
+def path_string(s):
+ """
+ Convert a Python string to a :py:class:`bytes` string identifying the same
+ path and which can be passed into an OpenSSL API accepting a filename.
+
+ :param s: An instance of :py:class:`bytes` or :py:class:`unicode`.
+
+ :return: An instance of :py:class:`bytes`.
+ """
+ if isinstance(s, binary_type):
+ return s
+ elif isinstance(s, text_type):
+ return s.encode(sys.getfilesystemencoding())
+ else:
+ raise TypeError("Path must be represented as bytes or unicode string")
+
+
+if PY3:
+ def byte_string(s):
+ return s.encode("charmap")
+else:
+ def byte_string(s):
+ return s
+
+
+# A marker object to observe whether some optional arguments are passed any
+# value or not.
+UNSPECIFIED = object()
+
+_TEXT_WARNING = (
+ text_type.__name__ + " for {0} is no longer accepted, use bytes"
+)
+
+
+def text_to_bytes_and_warn(label, obj):
+ """
+ If ``obj`` is text, emit a warning that it should be bytes instead and try
+ to convert it to bytes automatically.
+
+ :param str label: The name of the parameter from which ``obj`` was taken
+ (so a developer can easily find the source of the problem and correct
+ it).
+
+ :return: If ``obj`` is the text string type, a ``bytes`` object giving the
+ UTF-8 encoding of that text is returned. Otherwise, ``obj`` itself is
+ returned.
+ """
+ if isinstance(obj, text_type):
+ warnings.warn(
+ _TEXT_WARNING.format(label),
+ category=DeprecationWarning,
+ stacklevel=3
+ )
+ return obj.encode('utf-8')
+ return obj
--- /dev/null
+import datetime
+
+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,
+ PY3 as _PY3)
+
+from cryptography import x509
+from cryptography.hazmat.primitives.asymmetric import dsa, rsa
+from cryptography.utils import deprecated
+
+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,
+ UNSPECIFIED as _UNSPECIFIED,
+ text_to_bytes_and_warn as _text_to_bytes_and_warn,
+ make_assert as _make_assert,
+)
+
+__all__ = [
+ 'FILETYPE_PEM',
+ 'FILETYPE_ASN1',
+ 'FILETYPE_TEXT',
+ 'TYPE_RSA',
+ 'TYPE_DSA',
+ 'Error',
+ 'PKey',
+ 'get_elliptic_curves',
+ 'get_elliptic_curve',
+ 'X509Name',
+ 'X509Extension',
+ 'X509Req',
+ 'X509',
+ 'X509StoreFlags',
+ 'X509Store',
+ 'X509StoreContextError',
+ 'X509StoreContext',
+ 'load_certificate',
+ 'dump_certificate',
+ 'dump_publickey',
+ 'dump_privatekey',
+ 'Revoked',
+ 'CRL',
+ 'PKCS7',
+ 'PKCS12',
+ 'NetscapeSPKI',
+ 'load_publickey',
+ 'load_privatekey',
+ 'dump_certificate_request',
+ 'load_certificate_request',
+ 'sign',
+ 'verify',
+ 'dump_crl',
+ 'load_crl',
+ 'load_pkcs7_data',
+ 'load_pkcs12'
+]
+
+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
+TYPE_DH = _lib.EVP_PKEY_DH
+TYPE_EC = _lib.EVP_PKEY_EC
+
+
+class Error(Exception):
+ """
+ An error occurred in an `OpenSSL.crypto` API.
+ """
+
+
+_raise_current_error = partial(_exception_from_error_queue, Error)
+_openssl_assert = _make_assert(Error)
+
+
+def _get_backend():
+ """
+ Importing the backend from cryptography has the side effect of activating
+ the osrandom engine. This mutates the global state of OpenSSL in the
+ process and causes issues for various programs that use subinterpreters or
+ embed Python. By putting the import in this function we can avoid
+ triggering this side effect unless _get_backend is called.
+ """
+ from cryptography.hazmat.backends.openssl.backend import backend
+ return backend
+
+
+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)
+
+ _openssl_assert(bio != _ffi.NULL)
+
+ 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_TIME 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_TIME_set_string(boundary, when)
+ if set_result == 0:
+ raise ValueError("Invalid string")
+
+
+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 _X509NameInvalidator(object):
+ def __init__(self):
+ self._names = []
+
+ def add(self, name):
+ self._names.append(name)
+
+ def clear(self):
+ for name in self._names:
+ # Breaks the object, but also prevents UAF!
+ del name._name
+
+
+class PKey(object):
+ """
+ A class representing an DSA or RSA public key or key pair.
+ """
+ _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 to_cryptography_key(self):
+ """
+ Export as a ``cryptography`` key.
+
+ :rtype: One of ``cryptography``'s `key interfaces`_.
+
+ .. _key interfaces: https://cryptography.io/en/latest/hazmat/\
+ primitives/asymmetric/rsa/#key-interfaces
+
+ .. versionadded:: 16.1.0
+ """
+ backend = _get_backend()
+ if self._only_public:
+ return backend._evp_pkey_to_public_key(self._pkey)
+ else:
+ return backend._evp_pkey_to_private_key(self._pkey)
+
+ @classmethod
+ def from_cryptography_key(cls, crypto_key):
+ """
+ Construct based on a ``cryptography`` *crypto_key*.
+
+ :param crypto_key: A ``cryptography`` key.
+ :type crypto_key: One of ``cryptography``'s `key interfaces`_.
+
+ :rtype: PKey
+
+ .. versionadded:: 16.1.0
+ """
+ pkey = cls()
+ if not isinstance(crypto_key, (rsa.RSAPublicKey, rsa.RSAPrivateKey,
+ dsa.DSAPublicKey, dsa.DSAPrivateKey)):
+ raise TypeError("Unsupported key type")
+
+ pkey._pkey = crypto_key._evp_pkey
+ if isinstance(crypto_key, (rsa.RSAPublicKey, dsa.DSAPublicKey)):
+ pkey._only_public = True
+ pkey._initialized = True
+ return pkey
+
+ def generate_key(self, type, bits):
+ """
+ Generate a key pair of the given type, with the given number of bits.
+
+ This generates a key "into" the this object.
+
+ :param type: The key type.
+ :type type: :py:data:`TYPE_RSA` or :py:data:`TYPE_DSA`
+ :param bits: The number of bits.
+ :type bits: :py:data:`int` ``>= 0``
+ :raises TypeError: If :py:data:`type` or :py:data:`bits` isn't
+ of the appropriate type.
+ :raises ValueError: If the number of bits isn't an integer of
+ the appropriate size.
+ :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")
+
+ if type == TYPE_RSA:
+ if bits <= 0:
+ raise ValueError("Invalid number of bits")
+
+ # TODO Check error return
+ exponent = _lib.BN_new()
+ exponent = _ffi.gc(exponent, _lib.BN_free)
+ _lib.BN_set_word(exponent, _lib.RSA_F4)
+
+ rsa = _lib.RSA_new()
+
+ result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL)
+ _openssl_assert(result == 1)
+
+ result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa)
+ _openssl_assert(result == 1)
+
+ elif type == TYPE_DSA:
+ dsa = _lib.DSA_new()
+ _openssl_assert(dsa != _ffi.NULL)
+
+ dsa = _ffi.gc(dsa, _lib.DSA_free)
+ res = _lib.DSA_generate_parameters_ex(
+ dsa, bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL
+ )
+ _openssl_assert(res == 1)
+
+ _openssl_assert(_lib.DSA_generate_key(dsa) == 1)
+ _openssl_assert(_lib.EVP_PKEY_set1_DSA(self._pkey, dsa) == 1)
+ else:
+ raise Error("No such key type")
+
+ self._initialized = True
+
+ def check(self):
+ """
+ Check the consistency of an RSA private key.
+
+ This is the Python equivalent of OpenSSL's ``RSA_check_key``.
+
+ :return: ``True`` if key is consistent.
+
+ :raise OpenSSL.crypto.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.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 _lib.EVP_PKEY_id(self._pkey)
+
+ 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 = deprecated(
+ PKey, __name__,
+ "PKeyType has been deprecated, use PKey instead",
+ DeprecationWarning
+)
+
+
+class _EllipticCurve(object):
+ """
+ A representation of a supported elliptic curve.
+
+ @cvar _curves: :py:obj:`None` until an attempt is made to load the curves.
+ Thereafter, a :py:type:`set` containing :py:type:`_EllipticCurve`
+ instances each of which represents one curve supported by the system.
+ @type _curves: :py:type:`NoneType` or :py:type:`set`
+ """
+ _curves = None
+
+ if _PY3:
+ # This only necessary on Python 3. Morever, it is broken on Python 2.
+ def __ne__(self, other):
+ """
+ Implement cooperation with the right-hand side argument of ``!=``.
+
+ Python 3 seems to have dropped this cooperation in this very narrow
+ circumstance.
+ """
+ if isinstance(other, _EllipticCurve):
+ return super(_EllipticCurve, self).__ne__(other)
+ return NotImplemented
+
+ @classmethod
+ def _load_elliptic_curves(cls, lib):
+ """
+ Get the curves supported by OpenSSL.
+
+ :param lib: The OpenSSL library binding object.
+
+ :return: A :py:type:`set` of ``cls`` instances giving the names of the
+ elliptic curves the underlying library supports.
+ """
+ num_curves = lib.EC_get_builtin_curves(_ffi.NULL, 0)
+ builtin_curves = _ffi.new('EC_builtin_curve[]', num_curves)
+ # The return value on this call should be num_curves again. We
+ # could check it to make sure but if it *isn't* then.. what could
+ # we do? Abort the whole process, I suppose...? -exarkun
+ lib.EC_get_builtin_curves(builtin_curves, num_curves)
+ return set(
+ cls.from_nid(lib, c.nid)
+ for c in builtin_curves)
+
+ @classmethod
+ def _get_elliptic_curves(cls, lib):
+ """
+ Get, cache, and return the curves supported by OpenSSL.
+
+ :param lib: The OpenSSL library binding object.
+
+ :return: A :py:type:`set` of ``cls`` instances giving the names of the
+ elliptic curves the underlying library supports.
+ """
+ if cls._curves is None:
+ cls._curves = cls._load_elliptic_curves(lib)
+ return cls._curves
+
+ @classmethod
+ def from_nid(cls, lib, nid):
+ """
+ Instantiate a new :py:class:`_EllipticCurve` associated with the given
+ OpenSSL NID.
+
+ :param lib: The OpenSSL library binding object.
+
+ :param nid: The OpenSSL NID the resulting curve object will represent.
+ This must be a curve NID (and not, for example, a hash NID) or
+ subsequent operations will fail in unpredictable ways.
+ :type nid: :py:class:`int`
+
+ :return: The curve object.
+ """
+ return cls(lib, nid, _ffi.string(lib.OBJ_nid2sn(nid)).decode("ascii"))
+
+ def __init__(self, lib, nid, name):
+ """
+ :param _lib: The :py:mod:`cryptography` binding instance used to
+ interface with OpenSSL.
+
+ :param _nid: The OpenSSL NID identifying the curve this object
+ represents.
+ :type _nid: :py:class:`int`
+
+ :param name: The OpenSSL short name identifying the curve this object
+ represents.
+ :type name: :py:class:`unicode`
+ """
+ self._lib = lib
+ self._nid = nid
+ self.name = name
+
+ def __repr__(self):
+ return "<Curve %r>" % (self.name,)
+
+ def _to_EC_KEY(self):
+ """
+ Create a new OpenSSL EC_KEY structure initialized to use this curve.
+
+ The structure is automatically garbage collected when the Python object
+ is garbage collected.
+ """
+ key = self._lib.EC_KEY_new_by_curve_name(self._nid)
+ return _ffi.gc(key, _lib.EC_KEY_free)
+
+
+def get_elliptic_curves():
+ """
+ Return a set of objects representing the elliptic curves supported in the
+ OpenSSL build in use.
+
+ The curve objects have a :py:class:`unicode` ``name`` attribute by which
+ they identify themselves.
+
+ The curve objects are useful as values for the argument accepted by
+ :py:meth:`Context.set_tmp_ecdh` to specify which elliptical curve should be
+ used for ECDHE key exchange.
+ """
+ return _EllipticCurve._get_elliptic_curves(_lib)
+
+
+def get_elliptic_curve(name):
+ """
+ Return a single curve object selected by name.
+
+ See :py:func:`get_elliptic_curves` for information about curve objects.
+
+ :param name: The OpenSSL short name identifying the curve object to
+ retrieve.
+ :type name: :py:class:`unicode`
+
+ If the named curve is not supported then :py:class:`ValueError` is raised.
+ """
+ for curve in get_elliptic_curves():
+ if curve.name == name:
+ return curve
+ raise ValueError("unknown curve name", name)
+
+
+class X509Name(object):
+ """
+ An X.509 Distinguished Name.
+
+ :ivar countryName: The country of the entity.
+ :ivar C: Alias for :py:attr:`countryName`.
+
+ :ivar stateOrProvinceName: The state or province of the entity.
+ :ivar ST: Alias for :py:attr:`stateOrProvinceName`.
+
+ :ivar localityName: The locality of the entity.
+ :ivar L: Alias for :py:attr:`localityName`.
+
+ :ivar organizationName: The organization name of the entity.
+ :ivar O: Alias for :py:attr:`organizationName`.
+
+ :ivar organizationalUnitName: The organizational unit of the entity.
+ :ivar OU: Alias for :py:attr:`organizationalUnitName`
+
+ :ivar commonName: The common name of the entity.
+ :ivar CN: Alias for :py:attr:`commonName`.
+
+ :ivar emailAddress: The e-mail address of the entity.
+ """
+
+ def __init__(self, name):
+ """
+ Create a new X509Name, copying the given X509Name instance.
+
+ :param name: The name to copy.
+ :type name: :py:class:`X509Name`
+ """
+ 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)
+ _openssl_assert(data_length >= 0)
+
+ 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))
+ _openssl_assert(format_result != _ffi.NULL)
+
+ return "<X509Name object '%s'>" % (
+ _native(_ffi.string(result_buffer)),)
+
+ def hash(self):
+ """
+ Return an integer representation of the first four bytes of the
+ MD5 digest of the DER representation of the name.
+
+ This is the Python equivalent of OpenSSL's ``X509_NAME_hash``.
+
+ :return: The (integer) hash of this name.
+ :rtype: :py:class:`int`
+ """
+ return _lib.X509_NAME_hash(self._name)
+
+ def der(self):
+ """
+ Return the DER encoding of this name.
+
+ :return: The DER encoded form of this name.
+ :rtype: :py:class:`bytes`
+ """
+ result_buffer = _ffi.new('unsigned char**')
+ encode_result = _lib.i2d_X509_NAME(self._name, result_buffer)
+ _openssl_assert(encode_result >= 0)
+
+ string_result = _ffi.buffer(result_buffer[0], encode_result)[:]
+ _lib.OPENSSL_free(result_buffer[0])
+ return string_result
+
+ def get_components(self):
+ """
+ Returns the components of this name, as a sequence of 2-tuples.
+
+ :return: The components of this name.
+ :rtype: :py:class:`list` of ``name, value`` tuples.
+ """
+ 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)
+
+ # ffi.string does not handle strings containing NULL bytes
+ # (which may have been generated by old, broken software)
+ value = _ffi.buffer(_lib.ASN1_STRING_data(fval),
+ _lib.ASN1_STRING_length(fval))[:]
+ result.append((_ffi.string(name), value))
+
+ return result
+
+
+X509NameType = deprecated(
+ X509Name, __name__,
+ "X509NameType has been deprecated, use X509Name instead",
+ DeprecationWarning
+)
+
+
+class X509Extension(object):
+ """
+ An X.509 v3 certificate extension.
+ """
+
+ def __init__(self, type_name, critical, value, subject=None, issuer=None):
+ """
+ Initializes an X509 extension.
+
+ :param type_name: The name of the type of extension_ to create.
+ :type type_name: :py:data:`bytes`
+
+ :param bool critical: A flag indicating whether this is a critical
+ extension.
+
+ :param value: The value of the extension.
+ :type value: :py:data:`bytes`
+
+ :param subject: Optional X509 certificate to use as subject.
+ :type subject: :py:class:`X509`
+
+ :param issuer: Optional X509 certificate to use as issuer.
+ :type issuer: :py:class:`X509`
+
+ .. _extension: https://www.openssl.org/docs/manmaster/man5/
+ x509v3_config.html#STANDARD-EXTENSIONS
+ """
+ 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(
+ _lib.X509_EXTENSION_get_object(self._extension)
+ )
+
+ _prefixes = {
+ _lib.GEN_EMAIL: "email",
+ _lib.GEN_DNS: "DNS",
+ _lib.GEN_URI: "URI",
+ }
+
+ def _subjectAltNameString(self):
+ names = _ffi.cast(
+ "GENERAL_NAMES*", _lib.X509V3_EXT_d2i(self._extension)
+ )
+
+ names = _ffi.gc(names, _lib.GENERAL_NAMES_free)
+ 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)
+ _openssl_assert(print_result != 0)
+
+ return _native(_bio_to_string(bio))
+
+ def get_critical(self):
+ """
+ Returns the critical field of this X.509 extension.
+
+ :return: The critical field.
+ """
+ return _lib.X509_EXTENSION_get_critical(self._extension)
+
+ def get_short_name(self):
+ """
+ Returns the short type name of this X.509 extension.
+
+ The result is a byte string such as :py:const:`b"basicConstraints"`.
+
+ :return: The short type name.
+ :rtype: :py:data:`bytes`
+
+ .. versionadded:: 0.12
+ """
+ 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 X509 extension, encoded as ASN.1.
+
+ :return: The ASN.1 encoded data of this X509 extension.
+ :rtype: :py:data:`bytes`
+
+ .. versionadded:: 0.12
+ """
+ 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 = deprecated(
+ X509Extension, __name__,
+ "X509ExtensionType has been deprecated, use X509Extension instead",
+ DeprecationWarning
+)
+
+
+class X509Req(object):
+ """
+ An X.509 certificate signing requests.
+ """
+
+ def __init__(self):
+ req = _lib.X509_REQ_new()
+ self._req = _ffi.gc(req, _lib.X509_REQ_free)
+ # Default to version 0.
+ self.set_version(0)
+
+ def to_cryptography(self):
+ """
+ Export as a ``cryptography`` certificate signing request.
+
+ :rtype: ``cryptography.x509.CertificateSigningRequest``
+
+ .. versionadded:: 17.1.0
+ """
+ from cryptography.hazmat.backends.openssl.x509 import (
+ _CertificateSigningRequest
+ )
+ backend = _get_backend()
+ return _CertificateSigningRequest(backend, self._req)
+
+ @classmethod
+ def from_cryptography(cls, crypto_req):
+ """
+ Construct based on a ``cryptography`` *crypto_req*.
+
+ :param crypto_req: A ``cryptography`` X.509 certificate signing request
+ :type crypto_req: ``cryptography.x509.CertificateSigningRequest``
+
+ :rtype: X509Req
+
+ .. versionadded:: 17.1.0
+ """
+ if not isinstance(crypto_req, x509.CertificateSigningRequest):
+ raise TypeError("Must be a certificate signing request")
+
+ req = cls()
+ req._req = crypto_req._x509_req
+ return req
+
+ def set_pubkey(self, pkey):
+ """
+ Set the public key of the certificate signing request.
+
+ :param pkey: The public key to use.
+ :type pkey: :py:class:`PKey`
+
+ :return: ``None``
+ """
+ set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey)
+ _openssl_assert(set_result == 1)
+
+ def get_pubkey(self):
+ """
+ Get the public key of the certificate signing request.
+
+ :return: The public key.
+ :rtype: :py:class:`PKey`
+ """
+ pkey = PKey.__new__(PKey)
+ pkey._pkey = _lib.X509_REQ_get_pubkey(self._req)
+ _openssl_assert(pkey._pkey != _ffi.NULL)
+ 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 int version: The version number.
+ :return: ``None``
+ """
+ set_result = _lib.X509_REQ_set_version(self._req, version)
+ _openssl_assert(set_result == 1)
+
+ def get_version(self):
+ """
+ Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate
+ request.
+
+ :return: The value of the version subfield.
+ :rtype: :py:class:`int`
+ """
+ return _lib.X509_REQ_get_version(self._req)
+
+ def get_subject(self):
+ """
+ Return the subject of this certificate signing request.
+
+ This creates a new :class:`X509Name` that wraps the underlying subject
+ name field on the certificate signing request. Modifying it will modify
+ the underlying signing request, and will have the effect of modifying
+ any other :class:`X509Name` that refers to this subject.
+
+ :return: The subject of this certificate signing request.
+ :rtype: :class:`X509Name`
+ """
+ name = X509Name.__new__(X509Name)
+ name._name = _lib.X509_REQ_get_subject_name(self._req)
+ _openssl_assert(name._name != _ffi.NULL)
+
+ # 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 certificate signing request.
+
+ :param extensions: The X.509 extensions to add.
+ :type extensions: iterable of :py:class:`X509Extension`
+ :return: ``None``
+ """
+ stack = _lib.sk_X509_EXTENSION_new_null()
+ _openssl_assert(stack != _ffi.NULL)
+
+ 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)
+ _openssl_assert(add_result == 1)
+
+ def get_extensions(self):
+ """
+ Get X.509 extensions in the certificate signing request.
+
+ :return: The X.509 extensions in this request.
+ :rtype: :py:class:`list` of :py:class:`X509Extension` objects.
+
+ .. versionadded:: 0.15
+ """
+ exts = []
+ native_exts_obj = _lib.X509_REQ_get_extensions(self._req)
+ for i in range(_lib.sk_X509_EXTENSION_num(native_exts_obj)):
+ ext = X509Extension.__new__(X509Extension)
+ ext._extension = _lib.sk_X509_EXTENSION_value(native_exts_obj, i)
+ exts.append(ext)
+ return exts
+
+ def sign(self, pkey, digest):
+ """
+ Sign the certificate signing request with this key and digest type.
+
+ :param pkey: The key pair to sign with.
+ :type pkey: :py:class:`PKey`
+ :param digest: The name of the message digest to use for the signature,
+ e.g. :py:data:`b"sha256"`.
+ :type digest: :py:class:`bytes`
+ :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)
+ _openssl_assert(sign_result > 0)
+
+ def verify(self, pkey):
+ """
+ Verifies the signature on this certificate signing request.
+
+ :param PKey key: A public key.
+
+ :return: ``True`` if the signature is correct.
+ :rtype: bool
+
+ :raises 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 = deprecated(
+ X509Req, __name__,
+ "X509ReqType has been deprecated, use X509Req instead",
+ DeprecationWarning
+)
+
+
+class X509(object):
+ """
+ An X.509 certificate.
+ """
+ def __init__(self):
+ x509 = _lib.X509_new()
+ _openssl_assert(x509 != _ffi.NULL)
+ self._x509 = _ffi.gc(x509, _lib.X509_free)
+
+ self._issuer_invalidator = _X509NameInvalidator()
+ self._subject_invalidator = _X509NameInvalidator()
+
+ @classmethod
+ def _from_raw_x509_ptr(cls, x509):
+ cert = cls.__new__(cls)
+ cert._x509 = _ffi.gc(x509, _lib.X509_free)
+ cert._issuer_invalidator = _X509NameInvalidator()
+ cert._subject_invalidator = _X509NameInvalidator()
+ return cert
+
+ def to_cryptography(self):
+ """
+ Export as a ``cryptography`` certificate.
+
+ :rtype: ``cryptography.x509.Certificate``
+
+ .. versionadded:: 17.1.0
+ """
+ from cryptography.hazmat.backends.openssl.x509 import _Certificate
+ backend = _get_backend()
+ return _Certificate(backend, self._x509)
+
+ @classmethod
+ def from_cryptography(cls, crypto_cert):
+ """
+ Construct based on a ``cryptography`` *crypto_cert*.
+
+ :param crypto_key: A ``cryptography`` X.509 certificate.
+ :type crypto_key: ``cryptography.x509.Certificate``
+
+ :rtype: X509
+
+ .. versionadded:: 17.1.0
+ """
+ if not isinstance(crypto_cert, x509.Certificate):
+ raise TypeError("Must be a certificate")
+
+ cert = cls()
+ cert._x509 = crypto_cert._x509
+ return cert
+
+ def set_version(self, version):
+ """
+ Set the version number of the certificate. Note that the
+ version value is zero-based, eg. a value of 0 is V1.
+
+ :param version: The version number of the certificate.
+ :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 the version number of the certificate.
+
+ :return: The version number of the certificate.
+ :rtype: :py:class:`int`
+ """
+ return _lib.X509_get_version(self._x509)
+
+ def get_pubkey(self):
+ """
+ Get the public key of the certificate.
+
+ :return: The public key.
+ :rtype: :py:class:`PKey`
+ """
+ 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.
+ :type pkey: :py:class:`PKey`
+
+ :return: :py:data:`None`
+ """
+ if not isinstance(pkey, PKey):
+ raise TypeError("pkey must be a PKey instance")
+
+ set_result = _lib.X509_set_pubkey(self._x509, pkey._pkey)
+ _openssl_assert(set_result == 1)
+
+ def sign(self, pkey, digest):
+ """
+ Sign the certificate with this key and digest type.
+
+ :param pkey: The key to sign with.
+ :type pkey: :py:class:`PKey`
+
+ :param digest: The name of the message digest to use.
+ :type digest: :py:class:`bytes`
+
+ :return: :py:data:`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)
+ _openssl_assert(sign_result > 0)
+
+ def get_signature_algorithm(self):
+ """
+ Return the signature algorithm used in the certificate.
+
+ :return: The name of the algorithm.
+ :rtype: :py:class:`bytes`
+
+ :raises ValueError: If the signature algorithm is undefined.
+
+ .. versionadded:: 0.13
+ """
+ algor = _lib.X509_get0_tbs_sigalg(self._x509)
+ nid = _lib.OBJ_obj2nid(algor.algorithm)
+ 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, formatted as
+ :py:const:`b":"`-delimited hex pairs.
+ :rtype: :py:class:`bytes`
+ """
+ digest = _lib.EVP_get_digestbyname(_byte_string(digest_name))
+ if digest == _ffi.NULL:
+ raise ValueError("No such digest method")
+
+ result_buffer = _ffi.new("unsigned 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)
+ _openssl_assert(digest_result == 1)
+
+ 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.
+ :rtype: :py:class:`bytes`
+ """
+ return _lib.X509_subject_name_hash(self._x509)
+
+ def set_serial_number(self, serial):
+ """
+ Set the serial number of the certificate.
+
+ :param serial: The new serial number.
+ :type serial: :py:class:`int`
+
+ :return: :py:data`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)
+ _openssl_assert(set_result == 1)
+
+ def get_serial_number(self):
+ """
+ Return the serial number of this certificate.
+
+ :return: The serial number.
+ :rtype: int
+ """
+ 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 on which the certificate stops being valid.
+
+ :param int amount: The number of seconds by which to adjust the
+ timestamp.
+ :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):
+ """
+ Adjust the timestamp on which the certificate starts being valid.
+
+ :param amount: The number of seconds by which to adjust the timestamp.
+ :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.
+ :rtype: bool
+ """
+ time_string = _native(self.get_notAfter())
+ not_after = datetime.datetime.strptime(time_string, "%Y%m%d%H%M%SZ")
+
+ return not_after < datetime.datetime.utcnow()
+
+ def _get_boundary_time(self, which):
+ return _get_asn1_time(which(self._x509))
+
+ def get_notBefore(self):
+ """
+ Get the timestamp at which the certificate starts being valid.
+
+ The timestamp is formatted as an ASN.1 TIME::
+
+ YYYYMMDDhhmmssZ
+
+ :return: A timestamp string, or ``None`` if there is none.
+ :rtype: bytes or NoneType
+ """
+ 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 timestamp at which the certificate starts being valid.
+
+ The timestamp is formatted as an ASN.1 TIME::
+
+ YYYYMMDDhhmmssZ
+
+ :param bytes when: A timestamp string.
+ :return: ``None``
+ """
+ return self._set_boundary_time(_lib.X509_get_notBefore, when)
+
+ def get_notAfter(self):
+ """
+ Get the timestamp at which the certificate stops being valid.
+
+ The timestamp is formatted as an ASN.1 TIME::
+
+ YYYYMMDDhhmmssZ
+
+ :return: A timestamp string, or ``None`` if there is none.
+ :rtype: bytes or NoneType
+ """
+ return self._get_boundary_time(_lib.X509_get_notAfter)
+
+ def set_notAfter(self, when):
+ """
+ Set the timestamp at which the certificate stops being valid.
+
+ The timestamp is formatted as an ASN.1 TIME::
+
+ YYYYMMDDhhmmssZ
+
+ :param bytes when: A timestamp string.
+ :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)
+ _openssl_assert(name._name != _ffi.NULL)
+
+ # 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)
+ _openssl_assert(set_result == 1)
+
+ def get_issuer(self):
+ """
+ Return the issuer of this certificate.
+
+ This creates a new :class:`X509Name` that wraps the underlying issuer
+ name field on the certificate. Modifying it will modify the underlying
+ certificate, and will have the effect of modifying any other
+ :class:`X509Name` that refers to this issuer.
+
+ :return: The issuer of this certificate.
+ :rtype: :class:`X509Name`
+ """
+ name = self._get_name(_lib.X509_get_issuer_name)
+ self._issuer_invalidator.add(name)
+ return name
+
+ def set_issuer(self, issuer):
+ """
+ Set the issuer of this certificate.
+
+ :param issuer: The issuer.
+ :type issuer: :py:class:`X509Name`
+
+ :return: ``None``
+ """
+ self._set_name(_lib.X509_set_issuer_name, issuer)
+ self._issuer_invalidator.clear()
+
+ def get_subject(self):
+ """
+ Return the subject of this certificate.
+
+ This creates a new :class:`X509Name` that wraps the underlying subject
+ name field on the certificate. Modifying it will modify the underlying
+ certificate, and will have the effect of modifying any other
+ :class:`X509Name` that refers to this subject.
+
+ :return: The subject of this certificate.
+ :rtype: :class:`X509Name`
+ """
+ name = self._get_name(_lib.X509_get_subject_name)
+ self._subject_invalidator.add(name)
+ return name
+
+ def set_subject(self, subject):
+ """
+ Set the subject of this certificate.
+
+ :param subject: The subject.
+ :type subject: :py:class:`X509Name`
+
+ :return: ``None``
+ """
+ self._set_name(_lib.X509_set_subject_name, subject)
+ self._subject_invalidator.clear()
+
+ def get_extension_count(self):
+ """
+ Get the number of extensions on this certificate.
+
+ :return: The number of extensions.
+ :rtype: :py:class:`int`
+
+ .. versionadded:: 0.12
+ """
+ return _lib.X509_get_ext_count(self._x509)
+
+ def add_extensions(self, extensions):
+ """
+ Add extensions to the certificate.
+
+ :param extensions: The extensions to add.
+ :type extensions: An iterable of :py:class:`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.
+
+ Extensions on a certificate are kept in order. The index
+ parameter selects which extension will be returned.
+
+ :param int index: The index of the extension to retrieve.
+ :return: The extension at the specified index.
+ :rtype: :py:class:`X509Extension`
+ :raises IndexError: If the extension index was out of bounds.
+
+ .. versionadded:: 0.12
+ """
+ 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 = deprecated(
+ X509, __name__,
+ "X509Type has been deprecated, use X509 instead",
+ DeprecationWarning
+)
+
+
+class X509StoreFlags(object):
+ """
+ Flags for X509 verification, used to change the behavior of
+ :class:`X509Store`.
+
+ See `OpenSSL Verification Flags`_ for details.
+
+ .. _OpenSSL Verification Flags:
+ https://www.openssl.org/docs/manmaster/man3/X509_VERIFY_PARAM_set_flags.html
+ """
+ CRL_CHECK = _lib.X509_V_FLAG_CRL_CHECK
+ CRL_CHECK_ALL = _lib.X509_V_FLAG_CRL_CHECK_ALL
+ IGNORE_CRITICAL = _lib.X509_V_FLAG_IGNORE_CRITICAL
+ X509_STRICT = _lib.X509_V_FLAG_X509_STRICT
+ ALLOW_PROXY_CERTS = _lib.X509_V_FLAG_ALLOW_PROXY_CERTS
+ POLICY_CHECK = _lib.X509_V_FLAG_POLICY_CHECK
+ EXPLICIT_POLICY = _lib.X509_V_FLAG_EXPLICIT_POLICY
+ INHIBIT_MAP = _lib.X509_V_FLAG_INHIBIT_MAP
+ NOTIFY_POLICY = _lib.X509_V_FLAG_NOTIFY_POLICY
+ CHECK_SS_SIGNATURE = _lib.X509_V_FLAG_CHECK_SS_SIGNATURE
+ CB_ISSUER_CHECK = _lib.X509_V_FLAG_CB_ISSUER_CHECK
+
+
+class X509Store(object):
+ """
+ An X.509 store.
+
+ An X.509 store is used to describe a context in which to verify a
+ certificate. A description of a context may include a set of certificates
+ to trust, a set of certificate revocation lists, verification flags and
+ more.
+
+ An X.509 store, being only a description, cannot be used by itself to
+ verify a certificate. To carry out the actual verification process, see
+ :class:`X509StoreContext`.
+ """
+
+ def __init__(self):
+ store = _lib.X509_STORE_new()
+ self._store = _ffi.gc(store, _lib.X509_STORE_free)
+
+ def add_cert(self, cert):
+ """
+ Adds a trusted certificate to this store.
+
+ Adding a certificate with this method adds this certificate as a
+ *trusted* certificate.
+
+ :param X509 cert: The certificate to add to this store.
+
+ :raises TypeError: If the certificate is not an :class:`X509`.
+
+ :raises OpenSSL.crypto.Error: If OpenSSL was unhappy with your
+ certificate.
+
+ :return: ``None`` if the certificate was added successfully.
+ """
+ if not isinstance(cert, X509):
+ raise TypeError()
+
+ # As of OpenSSL 1.1.0i adding the same cert to the store more than
+ # once doesn't cause an error. Accordingly, this code now silences
+ # the error for OpenSSL < 1.1.0i as well.
+ if _lib.X509_STORE_add_cert(self._store, cert._x509) == 0:
+ code = _lib.ERR_peek_error()
+ err_reason = _lib.ERR_GET_REASON(code)
+ _openssl_assert(
+ err_reason == _lib.X509_R_CERT_ALREADY_IN_HASH_TABLE
+ )
+ _lib.ERR_clear_error()
+
+ def add_crl(self, crl):
+ """
+ Add a certificate revocation list to this store.
+
+ The certificate revocation lists added to a store will only be used if
+ the associated flags are configured to check certificate revocation
+ lists.
+
+ .. versionadded:: 16.1.0
+
+ :param CRL crl: The certificate revocation list to add to this store.
+ :return: ``None`` if the certificate revocation list was added
+ successfully.
+ """
+ _openssl_assert(_lib.X509_STORE_add_crl(self._store, crl._crl) != 0)
+
+ def set_flags(self, flags):
+ """
+ Set verification flags to this store.
+
+ Verification flags can be combined by oring them together.
+
+ .. note::
+
+ Setting a verification flag sometimes requires clients to add
+ additional information to the store, otherwise a suitable error will
+ be raised.
+
+ For example, in setting flags to enable CRL checking a
+ suitable CRL must be added to the store otherwise an error will be
+ raised.
+
+ .. versionadded:: 16.1.0
+
+ :param int flags: The verification flags to set on this store.
+ See :class:`X509StoreFlags` for available constants.
+ :return: ``None`` if the verification flags were successfully set.
+ """
+ _openssl_assert(_lib.X509_STORE_set_flags(self._store, flags) != 0)
+
+ def set_time(self, vfy_time):
+ """
+ Set the time against which the certificates are verified.
+
+ Normally the current time is used.
+
+ .. note::
+
+ For example, you can determine if a certificate was valid at a given
+ time.
+
+ .. versionadded:: 17.0.0
+
+ :param datetime vfy_time: The verification time to set on this store.
+ :return: ``None`` if the verification time was successfully set.
+ """
+ param = _lib.X509_VERIFY_PARAM_new()
+ param = _ffi.gc(param, _lib.X509_VERIFY_PARAM_free)
+
+ _lib.X509_VERIFY_PARAM_set_time(param, int(vfy_time.strftime('%s')))
+ _openssl_assert(_lib.X509_STORE_set1_param(self._store, param) != 0)
+
+
+X509StoreType = deprecated(
+ X509Store, __name__,
+ "X509StoreType has been deprecated, use X509Store instead",
+ DeprecationWarning
+)
+
+
+class X509StoreContextError(Exception):
+ """
+ An exception raised when an error occurred while verifying a certificate
+ using `OpenSSL.X509StoreContext.verify_certificate`.
+
+ :ivar certificate: The certificate which caused verificate failure.
+ :type certificate: :class:`X509`
+ """
+
+ def __init__(self, message, certificate):
+ super(X509StoreContextError, self).__init__(message)
+ self.certificate = certificate
+
+
+class X509StoreContext(object):
+ """
+ An X.509 store context.
+
+ An X.509 store context is used to carry out the actual verification process
+ of a certificate in a described context. For describing such a context, see
+ :class:`X509Store`.
+
+ :ivar _store_ctx: The underlying X509_STORE_CTX structure used by this
+ instance. It is dynamically allocated and automatically garbage
+ collected.
+ :ivar _store: See the ``store`` ``__init__`` parameter.
+ :ivar _cert: See the ``certificate`` ``__init__`` parameter.
+ :param X509Store store: The certificates which will be trusted for the
+ purposes of any verifications.
+ :param X509 certificate: The certificate to be verified.
+ """
+
+ def __init__(self, store, certificate):
+ store_ctx = _lib.X509_STORE_CTX_new()
+ self._store_ctx = _ffi.gc(store_ctx, _lib.X509_STORE_CTX_free)
+ self._store = store
+ self._cert = certificate
+ # Make the store context available for use after instantiating this
+ # class by initializing it now. Per testing, subsequent calls to
+ # :meth:`_init` have no adverse affect.
+ self._init()
+
+ def _init(self):
+ """
+ Set up the store context for a subsequent verification operation.
+
+ Calling this method more than once without first calling
+ :meth:`_cleanup` will leak memory.
+ """
+ ret = _lib.X509_STORE_CTX_init(
+ self._store_ctx, self._store._store, self._cert._x509, _ffi.NULL
+ )
+ if ret <= 0:
+ _raise_current_error()
+
+ def _cleanup(self):
+ """
+ Internally cleans up the store context.
+
+ The store context can then be reused with a new call to :meth:`_init`.
+ """
+ _lib.X509_STORE_CTX_cleanup(self._store_ctx)
+
+ def _exception_from_context(self):
+ """
+ Convert an OpenSSL native context error failure into a Python
+ exception.
+
+ When a call to native OpenSSL X509_verify_cert fails, additional
+ information about the failure can be obtained from the store context.
+ """
+ errors = [
+ _lib.X509_STORE_CTX_get_error(self._store_ctx),
+ _lib.X509_STORE_CTX_get_error_depth(self._store_ctx),
+ _native(_ffi.string(_lib.X509_verify_cert_error_string(
+ _lib.X509_STORE_CTX_get_error(self._store_ctx)))),
+ ]
+ # A context error should always be associated with a certificate, so we
+ # expect this call to never return :class:`None`.
+ _x509 = _lib.X509_STORE_CTX_get_current_cert(self._store_ctx)
+ _cert = _lib.X509_dup(_x509)
+ pycert = X509._from_raw_x509_ptr(_cert)
+ return X509StoreContextError(errors, pycert)
+
+ def set_store(self, store):
+ """
+ Set the context's X.509 store.
+
+ .. versionadded:: 0.15
+
+ :param X509Store store: The store description which will be used for
+ the purposes of any *future* verifications.
+ """
+ self._store = store
+
+ def verify_certificate(self):
+ """
+ Verify a certificate in a context.
+
+ .. versionadded:: 0.15
+
+ :raises X509StoreContextError: If an error occurred when validating a
+ certificate in the context. Sets ``certificate`` attribute to
+ indicate which certificate caused the error.
+ """
+ # Always re-initialize the store context in case
+ # :meth:`verify_certificate` is called multiple times.
+ #
+ # :meth:`_init` is called in :meth:`__init__` so _cleanup is called
+ # before _init to ensure memory is not leaked.
+ self._cleanup()
+ self._init()
+ ret = _lib.X509_verify_cert(self._store_ctx)
+ self._cleanup()
+ if ret <= 0:
+ raise self._exception_from_context()
+
+
+def load_certificate(type, buffer):
+ """
+ Load a certificate (X509) from the string *buffer* encoded with the
+ type *type*.
+
+ :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)
+
+ :param bytes buffer: The buffer the certificate is stored in
+
+ :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()
+
+ return X509._from_raw_x509_ptr(x509)
+
+
+def dump_certificate(type, cert):
+ """
+ Dump the certificate *cert* into a buffer string encoded with the type
+ *type*.
+
+ :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")
+
+ assert result_code == 1
+ return _bio_to_string(bio)
+
+
+def dump_publickey(type, pkey):
+ """
+ Dump a public key to a buffer.
+
+ :param type: The file type (one of :data:`FILETYPE_PEM` or
+ :data:`FILETYPE_ASN1`).
+ :param PKey pkey: The public key to dump
+ :return: The buffer with the dumped key in it.
+ :rtype: bytes
+ """
+ bio = _new_mem_buf()
+ if type == FILETYPE_PEM:
+ write_bio = _lib.PEM_write_bio_PUBKEY
+ elif type == FILETYPE_ASN1:
+ write_bio = _lib.i2d_PUBKEY_bio
+ else:
+ raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1")
+
+ result_code = write_bio(bio, pkey._pkey)
+ if result_code != 1: # pragma: no cover
+ _raise_current_error()
+
+ return _bio_to_string(bio)
+
+
+def dump_privatekey(type, pkey, cipher=None, passphrase=None):
+ """
+ Dump the private key *pkey* into a buffer string encoded with the type
+ *type*. Optionally (if *type* is :const:`FILETYPE_PEM`) encrypting it
+ using *cipher* and *passphrase*.
+
+ :param type: The file type (one of :const:`FILETYPE_PEM`,
+ :const:`FILETYPE_ASN1`, or :const:`FILETYPE_TEXT`)
+ :param PKey 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: bytes
+ """
+ bio = _new_mem_buf()
+
+ if not isinstance(pkey, PKey):
+ raise TypeError("pkey must be a PKey")
+
+ 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:
+ if _lib.EVP_PKEY_id(pkey._pkey) != _lib.EVP_PKEY_RSA:
+ raise TypeError("Only RSA keys are supported for FILETYPE_TEXT")
+
+ rsa = _ffi.gc(
+ _lib.EVP_PKEY_get1_RSA(pkey._pkey),
+ _lib.RSA_free
+ )
+ result_code = _lib.RSA_print(bio, rsa, 0)
+ else:
+ raise ValueError(
+ "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
+ "FILETYPE_TEXT")
+
+ _openssl_assert(result_code != 0)
+
+ return _bio_to_string(bio)
+
+
+class Revoked(object):
+ """
+ A certificate revocation.
+ """
+ # https://www.openssl.org/docs/manmaster/man5/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.
+
+ The serial number is formatted as a hexadecimal number encoded in
+ ASCII.
+
+ :param bytes hex_str: The new serial number.
+
+ :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):
+ """
+ Get the serial number.
+
+ The serial number is formatted as a hexadecimal number encoded in
+ ASCII.
+
+ :return: The serial number.
+ :rtype: bytes
+ """
+ bio = _new_mem_buf()
+
+ asn1_int = _lib.X509_REVOKED_get0_serialNumber(self._revoked)
+ _openssl_assert(asn1_int != _ffi.NULL)
+ result = _lib.i2a_ASN1_INTEGER(bio, asn1_int)
+ _openssl_assert(result >= 0)
+ return _bio_to_string(bio)
+
+ def _delete_reason(self):
+ for i in range(_lib.X509_REVOKED_get_ext_count(self._revoked)):
+ ext = _lib.X509_REVOKED_get_ext(self._revoked, i)
+ obj = _lib.X509_EXTENSION_get_object(ext)
+ if _lib.OBJ_obj2nid(obj) == _lib.NID_crl_reason:
+ _lib.X509_EXTENSION_free(ext)
+ _lib.X509_REVOKED_delete_ext(self._revoked, i)
+ break
+
+ def set_reason(self, reason):
+ """
+ Set the reason of this revocation.
+
+ If :data:`reason` is ``None``, delete the reason instead.
+
+ :param reason: The reason string.
+ :type reason: :class:`bytes` or :class:`NoneType`
+
+ :return: ``None``
+
+ .. seealso::
+
+ :meth:`all_reasons`, which gives you a list of all supported
+ reasons which you might pass to this method.
+ """
+ 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()
+ _openssl_assert(new_reason_ext != _ffi.NULL)
+ new_reason_ext = _ffi.gc(new_reason_ext, _lib.ASN1_ENUMERATED_free)
+
+ set_result = _lib.ASN1_ENUMERATED_set(new_reason_ext, reason_code)
+ _openssl_assert(set_result != _ffi.NULL)
+
+ self._delete_reason()
+ add_result = _lib.X509_REVOKED_add1_ext_i2d(
+ self._revoked, _lib.NID_crl_reason, new_reason_ext, 0, 0)
+ _openssl_assert(add_result == 1)
+
+ def get_reason(self):
+ """
+ Get the reason of this revocation.
+
+ :return: The reason, or ``None`` if there is none.
+ :rtype: bytes or NoneType
+
+ .. seealso::
+
+ :meth:`all_reasons`, which gives you a list of all supported
+ reasons this method might return.
+ """
+ for i in range(_lib.X509_REVOKED_get_ext_count(self._revoked)):
+ ext = _lib.X509_REVOKED_get_ext(self._revoked, i)
+ obj = _lib.X509_EXTENSION_get_object(ext)
+ if _lib.OBJ_obj2nid(obj) == _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, _lib.X509_EXTENSION_get_data(ext)
+ )
+ _openssl_assert(print_result != 0)
+
+ return _bio_to_string(bio)
+
+ def all_reasons(self):
+ """
+ Return a list of all the supported reason strings.
+
+ This list is a copy; modifying it does not change the supported reason
+ strings.
+
+ :return: A list of reason strings.
+ :rtype: :class:`list` of :class:`bytes`
+ """
+ return self._crl_reasons[:]
+
+ def set_rev_date(self, when):
+ """
+ Set the revocation timestamp.
+
+ :param bytes when: The timestamp of the revocation,
+ as ASN.1 TIME.
+ :return: ``None``
+ """
+ dt = _lib.X509_REVOKED_get0_revocationDate(self._revoked)
+ return _set_asn1_time(dt, when)
+
+ def get_rev_date(self):
+ """
+ Get the revocation timestamp.
+
+ :return: The timestamp of the revocation, as ASN.1 TIME.
+ :rtype: bytes
+ """
+ dt = _lib.X509_REVOKED_get0_revocationDate(self._revoked)
+ return _get_asn1_time(dt)
+
+
+class CRL(object):
+ """
+ A certificate revocation list.
+ """
+
+ def __init__(self):
+ crl = _lib.X509_CRL_new()
+ self._crl = _ffi.gc(crl, _lib.X509_CRL_free)
+
+ def to_cryptography(self):
+ """
+ Export as a ``cryptography`` CRL.
+
+ :rtype: ``cryptography.x509.CertificateRevocationList``
+
+ .. versionadded:: 17.1.0
+ """
+ from cryptography.hazmat.backends.openssl.x509 import (
+ _CertificateRevocationList
+ )
+ backend = _get_backend()
+ return _CertificateRevocationList(backend, self._crl)
+
+ @classmethod
+ def from_cryptography(cls, crypto_crl):
+ """
+ Construct based on a ``cryptography`` *crypto_crl*.
+
+ :param crypto_crl: A ``cryptography`` certificate revocation list
+ :type crypto_crl: ``cryptography.x509.CertificateRevocationList``
+
+ :rtype: CRL
+
+ .. versionadded:: 17.1.0
+ """
+ if not isinstance(crypto_crl, x509.CertificateRevocationList):
+ raise TypeError("Must be a certificate revocation list")
+
+ crl = cls()
+ crl._crl = crypto_crl._x509_crl
+ return crl
+
+ def get_revoked(self):
+ """
+ Return the revocations in this certificate revocation list.
+
+ These revocations will be provided by value, not by reference.
+ That means it's okay to mutate them: it won't affect this CRL.
+
+ :return: The revocations in this CRL.
+ :rtype: :class:`tuple` of :class:`Revocation`
+ """
+ results = []
+ revoked_stack = _lib.X509_CRL_get_REVOKED(self._crl)
+ for i in range(_lib.sk_X509_REVOKED_num(revoked_stack)):
+ revoked = _lib.sk_X509_REVOKED_value(revoked_stack, i)
+ revoked_copy = _lib.Cryptography_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
+
+ This revocation will be added by value, not by reference. That
+ means it's okay to mutate it after adding: it won't affect
+ this CRL.
+
+ :param Revoked revoked: The new revocation.
+ :return: ``None``
+ """
+ copy = _lib.Cryptography_X509_REVOKED_dup(revoked._revoked)
+ _openssl_assert(copy != _ffi.NULL)
+
+ add_result = _lib.X509_CRL_add0_revoked(self._crl, copy)
+ _openssl_assert(add_result != 0)
+
+ def get_issuer(self):
+ """
+ Get the CRL's issuer.
+
+ .. versionadded:: 16.1.0
+
+ :rtype: X509Name
+ """
+ _issuer = _lib.X509_NAME_dup(_lib.X509_CRL_get_issuer(self._crl))
+ _openssl_assert(_issuer != _ffi.NULL)
+ _issuer = _ffi.gc(_issuer, _lib.X509_NAME_free)
+ issuer = X509Name.__new__(X509Name)
+ issuer._name = _issuer
+ return issuer
+
+ def set_version(self, version):
+ """
+ Set the CRL version.
+
+ .. versionadded:: 16.1.0
+
+ :param int version: The version of the CRL.
+ :return: ``None``
+ """
+ _openssl_assert(_lib.X509_CRL_set_version(self._crl, version) != 0)
+
+ def _set_boundary_time(self, which, when):
+ return _set_asn1_time(which(self._crl), when)
+
+ def set_lastUpdate(self, when):
+ """
+ Set when the CRL was last updated.
+
+ The timestamp is formatted as an ASN.1 TIME::
+
+ YYYYMMDDhhmmssZ
+
+ .. versionadded:: 16.1.0
+
+ :param bytes when: A timestamp string.
+ :return: ``None``
+ """
+ return self._set_boundary_time(_lib.X509_CRL_get_lastUpdate, when)
+
+ def set_nextUpdate(self, when):
+ """
+ Set when the CRL will next be udpated.
+
+ The timestamp is formatted as an ASN.1 TIME::
+
+ YYYYMMDDhhmmssZ
+
+ .. versionadded:: 16.1.0
+
+ :param bytes when: A timestamp string.
+ :return: ``None``
+ """
+ return self._set_boundary_time(_lib.X509_CRL_get_nextUpdate, when)
+
+ def sign(self, issuer_cert, issuer_key, digest):
+ """
+ Sign the CRL.
+
+ Signing a CRL enables clients to associate the CRL itself with an
+ issuer. Before a CRL is meaningful to other OpenSSL functions, it must
+ be signed by an issuer.
+
+ This method implicitly sets the issuer's name based on the issuer
+ certificate and private key used to sign the CRL.
+
+ .. versionadded:: 16.1.0
+
+ :param X509 issuer_cert: The issuer's certificate.
+ :param PKey issuer_key: The issuer's private key.
+ :param bytes digest: The digest method to sign the CRL with.
+ """
+ digest_obj = _lib.EVP_get_digestbyname(digest)
+ _openssl_assert(digest_obj != _ffi.NULL)
+ _lib.X509_CRL_set_issuer_name(
+ self._crl, _lib.X509_get_subject_name(issuer_cert._x509))
+ _lib.X509_CRL_sort(self._crl)
+ result = _lib.X509_CRL_sign(self._crl, issuer_key._pkey, digest_obj)
+ _openssl_assert(result != 0)
+
+ def export(self, cert, key, type=FILETYPE_PEM, days=100,
+ digest=_UNSPECIFIED):
+ """
+ Export the CRL as a string.
+
+ :param X509 cert: The certificate used to sign the CRL.
+ :param PKey key: The key used to sign the CRL.
+ :param int type: The export format, either :data:`FILETYPE_PEM`,
+ :data:`FILETYPE_ASN1`, or :data:`FILETYPE_TEXT`.
+ :param int days: The number of days until the next update of this CRL.
+ :param bytes digest: The name of the message digest to use (eg
+ ``b"sha2566"``).
+ :rtype: bytes
+ """
+
+ 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")
+
+ if digest is _UNSPECIFIED:
+ raise TypeError("digest must be provided")
+
+ digest_obj = _lib.EVP_get_digestbyname(digest)
+ if digest_obj == _ffi.NULL:
+ raise ValueError("No such digest method")
+
+ bio = _lib.BIO_new(_lib.BIO_s_mem())
+ _openssl_assert(bio != _ffi.NULL)
+
+ # A scratch time object to give different values to different CRL
+ # fields
+ sometime = _lib.ASN1_TIME_new()
+ _openssl_assert(sometime != _ffi.NULL)
+
+ _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, digest_obj)
+ if not sign_result:
+ _raise_current_error()
+
+ return dump_crl(type, self)
+
+
+CRLType = deprecated(
+ CRL, __name__,
+ "CRLType has been deprecated, use CRL instead",
+ DeprecationWarning
+)
+
+
+class PKCS7(object):
+ def type_is_signed(self):
+ """
+ Check if this NID_pkcs7_signed object
+
+ :return: True if the PKCS7 is of type signed
+ """
+ return bool(_lib.PKCS7_type_is_signed(self._pkcs7))
+
+ def type_is_enveloped(self):
+ """
+ Check if this NID_pkcs7_enveloped object
+
+ :returns: True if the PKCS7 is of type enveloped
+ """
+ return bool(_lib.PKCS7_type_is_enveloped(self._pkcs7))
+
+ def type_is_signedAndEnveloped(self):
+ """
+ Check if this NID_pkcs7_signedAndEnveloped object
+
+ :returns: True if the PKCS7 is of type signedAndEnveloped
+ """
+ return bool(_lib.PKCS7_type_is_signedAndEnveloped(self._pkcs7))
+
+ def type_is_data(self):
+ """
+ Check if this NID_pkcs7_data object
+
+ :return: True if the PKCS7 is of type data
+ """
+ return bool(_lib.PKCS7_type_is_data(self._pkcs7))
+
+ 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 = deprecated(
+ PKCS7, __name__,
+ "PKCS7Type has been deprecated, use PKCS7 instead",
+ DeprecationWarning
+)
+
+
+class PKCS12(object):
+ """
+ A PKCS #12 archive.
+ """
+
+ def __init__(self):
+ self._pkey = None
+ self._cert = None
+ self._cacerts = None
+ self._friendlyname = None
+
+ def get_certificate(self):
+ """
+ Get the certificate in the PKCS #12 structure.
+
+ :return: The certificate, or :py:const:`None` if there is none.
+ :rtype: :py:class:`X509` or :py:const:`None`
+ """
+ return self._cert
+
+ def set_certificate(self, cert):
+ """
+ Set the certificate in the PKCS #12 structure.
+
+ :param cert: The new certificate, or :py:const:`None` to unset it.
+ :type cert: :py:class:`X509` or :py:const:`None`
+
+ :return: ``None``
+ """
+ if not isinstance(cert, X509):
+ raise TypeError("cert must be an X509 instance")
+ self._cert = cert
+
+ def get_privatekey(self):
+ """
+ Get the private key in the PKCS #12 structure.
+
+ :return: The private key, or :py:const:`None` if there is none.
+ :rtype: :py:class:`PKey`
+ """
+ return self._pkey
+
+ def set_privatekey(self, pkey):
+ """
+ Set the certificate portion of the PKCS #12 structure.
+
+ :param pkey: The new private key, or :py:const:`None` to unset it.
+ :type pkey: :py:class:`PKey` or :py:const:`None`
+
+ :return: ``None``
+ """
+ if not isinstance(pkey, PKey):
+ raise TypeError("pkey must be a PKey instance")
+ self._pkey = pkey
+
+ def get_ca_certificates(self):
+ """
+ Get the CA certificates in the PKCS #12 structure.
+
+ :return: A tuple with the CA certificates in the chain, or
+ :py:const:`None` if there are none.
+ :rtype: :py:class:`tuple` of :py:class:`X509` or :py:const:`None`
+ """
+ if self._cacerts is not None:
+ return tuple(self._cacerts)
+
+ def set_ca_certificates(self, cacerts):
+ """
+ Replace or set the CA certificates within the PKCS12 object.
+
+ :param cacerts: The new CA certificates, or :py:const:`None` to unset
+ them.
+ :type cacerts: An iterable of :py:class:`X509` or :py:const:`None`
+
+ :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):
+ """
+ Set the friendly name in the PKCS #12 structure.
+
+ :param name: The new friendly name, or :py:const:`None` to unset.
+ :type name: :py:class:`bytes` or :py:const:`None`
+
+ :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):
+ """
+ Get the friendly name in the PKCS# 12 structure.
+
+ :returns: The friendly name, or :py:const:`None` if there is none.
+ :rtype: :py:class:`bytes` or :py:const:`None`
+ """
+ return self._friendlyname
+
+ def export(self, passphrase=None, iter=2048, maciter=1):
+ """
+ Dump a PKCS12 object as a string.
+
+ For more information, see the :c:func:`PKCS12_create` man page.
+
+ :param passphrase: The passphrase used to encrypt the structure. Unlike
+ some other passphrase arguments, this *must* be a string, not a
+ callback.
+ :type passphrase: :py:data:`bytes`
+
+ :param iter: Number of times to repeat the encryption step.
+ :type iter: :py:data:`int`
+
+ :param maciter: Number of times to repeat the MAC step.
+ :type maciter: :py:data:`int`
+
+ :return: The string representation of the PKCS #12 structure.
+ :rtype:
+ """
+ passphrase = _text_to_bytes_and_warn("passphrase", passphrase)
+
+ 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 = deprecated(
+ PKCS12, __name__,
+ "PKCS12Type has been deprecated, use PKCS12 instead",
+ DeprecationWarning
+)
+
+
+class NetscapeSPKI(object):
+ """
+ A Netscape SPKI 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 with this key and digest type.
+
+ :param pkey: The private key to sign with.
+ :type pkey: :py:class:`PKey`
+
+ :param digest: The message digest to use.
+ :type digest: :py:class:`bytes`
+
+ :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
+ )
+ _openssl_assert(sign_result > 0)
+
+ def verify(self, key):
+ """
+ Verifies a signature on a certificate request.
+
+ :param PKey key: The public key that signature is supposedly from.
+
+ :return: ``True`` if the signature is correct.
+ :rtype: bool
+
+ :raises OpenSSL.crypto.Error: If the signature is invalid, or there was
+ 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 representation of this SPKI object.
+
+ :return: The base64 encoded string.
+ :rtype: :py:class:`bytes`
+ """
+ encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki)
+ result = _ffi.string(encoded)
+ _lib.OPENSSL_free(encoded)
+ return result
+
+ def get_pubkey(self):
+ """
+ Get the public key of this certificate.
+
+ :return: The public key.
+ :rtype: :py:class:`PKey`
+ """
+ pkey = PKey.__new__(PKey)
+ pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki)
+ _openssl_assert(pkey._pkey != _ffi.NULL)
+ 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)
+ _openssl_assert(set_result == 1)
+
+
+NetscapeSPKIType = deprecated(
+ NetscapeSPKI, __name__,
+ "NetscapeSPKIType has been deprecated, use NetscapeSPKI instead",
+ DeprecationWarning
+)
+
+
+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 a byte string or a 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 a byte string or a callable."
+ )
+
+ def raise_if_problem(self, exceptionType=Error):
+ if self._problems:
+
+ # Flush the OpenSSL error queue
+ try:
+ _exception_from_error_queue(exceptionType)
+ except exceptionType:
+ pass
+
+ raise self._problems.pop(0)
+
+ 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_publickey(type, buffer):
+ """
+ Load a public key from a buffer.
+
+ :param type: The file type (one of :data:`FILETYPE_PEM`,
+ :data:`FILETYPE_ASN1`).
+ :param buffer: The buffer the key is stored in.
+ :type buffer: A Python string object, either unicode or bytestring.
+ :return: The PKey object.
+ :rtype: :class:`PKey`
+ """
+ if isinstance(buffer, _text_type):
+ buffer = buffer.encode("ascii")
+
+ bio = _new_mem_buf(buffer)
+
+ if type == FILETYPE_PEM:
+ evp_pkey = _lib.PEM_read_bio_PUBKEY(
+ bio, _ffi.NULL, _ffi.NULL, _ffi.NULL)
+ elif type == FILETYPE_ASN1:
+ evp_pkey = _lib.d2i_PUBKEY_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)
+ pkey._only_public = True
+ return pkey
+
+
+def load_privatekey(type, buffer, passphrase=None):
+ """
+ Load a private key (PKey) from the string *buffer* encoded with the type
+ *type*.
+
+ :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 the certificate request *req* into a buffer string encoded with the
+ type *type*.
+
+ :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"
+ )
+
+ _openssl_assert(result_code != 0)
+
+ return _bio_to_string(bio)
+
+
+def load_certificate_request(type, buffer):
+ """
+ Load a certificate request (X509Req) from the string *buffer* encoded with
+ the type *type*.
+
+ :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")
+
+ _openssl_assert(req != _ffi.NULL)
+
+ x509req = X509Req.__new__(X509Req)
+ x509req._req = _ffi.gc(req, _lib.X509_REQ_free)
+ return x509req
+
+
+def sign(pkey, data, digest):
+ """
+ Sign a data string using the given key and message digest.
+
+ :param pkey: PKey to sign with
+ :param data: data to be signed
+ :param digest: message digest to use
+ :return: signature
+
+ .. versionadded:: 0.11
+ """
+ data = _text_to_bytes_and_warn("data", data)
+
+ digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
+ if digest_obj == _ffi.NULL:
+ raise ValueError("No such digest method")
+
+ md_ctx = _lib.Cryptography_EVP_MD_CTX_new()
+ md_ctx = _ffi.gc(md_ctx, _lib.Cryptography_EVP_MD_CTX_free)
+
+ _lib.EVP_SignInit(md_ctx, digest_obj)
+ _lib.EVP_SignUpdate(md_ctx, data, len(data))
+
+ length = _lib.EVP_PKEY_size(pkey._pkey)
+ _openssl_assert(length > 0)
+ signature_buffer = _ffi.new("unsigned char[]", length)
+ signature_length = _ffi.new("unsigned int *")
+ final_result = _lib.EVP_SignFinal(
+ md_ctx, signature_buffer, signature_length, pkey._pkey)
+ _openssl_assert(final_result == 1)
+
+ return _ffi.buffer(signature_buffer, signature_length[0])[:]
+
+
+def verify(cert, signature, data, digest):
+ """
+ Verify the signature for a data string.
+
+ :param cert: signing certificate (X509 object) corresponding to the
+ private key which generated the signature.
+ :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.
+
+ .. versionadded:: 0.11
+ """
+ data = _text_to_bytes_and_warn("data", data)
+
+ 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)
+ _openssl_assert(pkey != _ffi.NULL)
+ pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free)
+
+ md_ctx = _lib.Cryptography_EVP_MD_CTX_new()
+ md_ctx = _ffi.gc(md_ctx, _lib.Cryptography_EVP_MD_CTX_free)
+
+ _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 dump_crl(type, crl):
+ """
+ Dump a certificate revocation list to a buffer.
+
+ :param type: The file type (one of ``FILETYPE_PEM``, ``FILETYPE_ASN1``, or
+ ``FILETYPE_TEXT``).
+ :param CRL crl: The CRL to dump.
+
+ :return: The buffer with the CRL.
+ :rtype: bytes
+ """
+ bio = _new_mem_buf()
+
+ if type == FILETYPE_PEM:
+ ret = _lib.PEM_write_bio_X509_CRL(bio, crl._crl)
+ elif type == FILETYPE_ASN1:
+ ret = _lib.i2d_X509_CRL_bio(bio, crl._crl)
+ elif type == FILETYPE_TEXT:
+ ret = _lib.X509_CRL_print(bio, crl._crl)
+ else:
+ raise ValueError(
+ "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or "
+ "FILETYPE_TEXT")
+
+ assert ret == 1
+ return _bio_to_string(bio)
+
+
+def load_crl(type, buffer):
+ """
+ Load Certificate Revocation List (CRL) data from a string *buffer*.
+ *buffer* encoded with the type *type*.
+
+ :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 = _ffi.gc(crl, _lib.X509_CRL_free)
+ return result
+
+
+def load_pkcs7_data(type, buffer):
+ """
+ Load pkcs7 data from the string *buffer* encoded with the type
+ *type*.
+
+ :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:
+ pkcs7 = _lib.d2i_PKCS7_bio(bio, _ffi.NULL)
+ else:
+ 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=None):
+ """
+ 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`.
+
+ :param buffer: The buffer the certificate is stored in
+ :param passphrase: (Optional) The password to decrypt the PKCS12 lump
+ :returns: The PKCS12 object
+ """
+ passphrase = _text_to_bytes_and_warn("passphrase", passphrase)
+
+ if isinstance(buffer, _text_type):
+ buffer = buffer.encode("ascii")
+
+ bio = _new_mem_buf(buffer)
+
+ # Use null passphrase if passphrase is None or empty string. With PKCS#12
+ # password based encryption no password and a zero length password are two
+ # different things, but OpenSSL implementation will try both to figure out
+ # which one works.
+ if not passphrase:
+ passphrase = _ffi.NULL
+
+ 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._from_raw_x509_ptr(cert[0])
+
+ 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)):
+ x509 = _lib.sk_X509_value(cacerts, i)
+ pycacert = X509._from_raw_x509_ptr(x509)
+ 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
+
+
+# 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()
+
+
+# Set the default string mask to match OpenSSL upstream (since 2005) and
+# RFC5280 recommendations.
+_lib.ASN1_STRING_set_default_mask_asc(b'utf8only')
--- /dev/null
+from __future__ import print_function
+
+import ssl
+import sys
+
+import OpenSSL.SSL
+import cffi
+import cryptography
+
+from . import version
+
+
+_env_info = u"""\
+pyOpenSSL: {pyopenssl}
+cryptography: {cryptography}
+cffi: {cffi}
+cryptography's compiled against OpenSSL: {crypto_openssl_compile}
+cryptography's linked OpenSSL: {crypto_openssl_link}
+Pythons's OpenSSL: {python_openssl}
+Python executable: {python}
+Python version: {python_version}
+Platform: {platform}
+sys.path: {sys_path}""".format(
+ pyopenssl=version.__version__,
+ crypto_openssl_compile=OpenSSL._util.ffi.string(
+ OpenSSL._util.lib.OPENSSL_VERSION_TEXT,
+ ).decode("ascii"),
+ crypto_openssl_link=OpenSSL.SSL.SSLeay_version(
+ OpenSSL.SSL.SSLEAY_VERSION
+ ).decode("ascii"),
+ python_openssl=getattr(ssl, "OPENSSL_VERSION", "n/a"),
+ cryptography=cryptography.__version__,
+ cffi=cffi.__version__,
+ python=sys.executable,
+ python_version=sys.version,
+ platform=sys.platform,
+ sys_path=sys.path,
+)
+
+
+if __name__ == "__main__":
+ print(_env_info)
--- /dev/null
+"""
+PRNG management routines, thin wrappers.
+"""
+
+from OpenSSL._util import lib as _lib
+
+
+def add(buffer, 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`.
+
+ This function is only relevant if you are forking Python processes and
+ need to reseed the CSPRNG after fork.
+
+ :param buffer: Buffer with random data.
+ :param entropy: The entropy (in bytes) measurement of the buffer.
+
+ :return: :obj:`None`
+ """
+ if not isinstance(buffer, bytes):
+ raise TypeError("buffer must be a byte string")
+
+ if not isinstance(entropy, int):
+ raise TypeError("entropy must be an integer")
+
+ _lib.RAND_add(buffer, len(buffer), entropy)
+
+
+def status():
+ """
+ Check whether the PRNG has been seeded with enough data.
+
+ :return: 1 if the PRNG is seeded enough, 0 otherwise.
+ """
+ return _lib.RAND_status()
--- /dev/null
+import warnings
+from threading import RLock as _RLock
+
+from OpenSSL import SSL as _ssl
+
+
+warnings.warn(
+ "OpenSSL.tsafe is deprecated and will be removed",
+ DeprecationWarning, stacklevel=3
+)
+
+
+class Connection:
+ def __init__(self, *args):
+ self._ssl_conn = _ssl.Connection(*args)
+ self._lock = _RLock()
+
+ for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read',
+ 'renegotiate', 'bind', 'listen', 'connect', 'accept',
+ 'setblocking', 'fileno', 'shutdown', 'close', 'get_cipher_list',
+ 'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
+ 'makefile', 'get_app_data', 'set_app_data', 'state_string',
+ 'sock_shutdown', 'get_peer_certificate', 'get_peer_cert_chain',
+ 'want_read', 'want_write', 'set_connect_state',
+ 'set_accept_state', 'connect_ex', 'sendall'):
+ exec("""def %s(self, *args):
+ self._lock.acquire()
+ try:
+ return self._ssl_conn.%s(*args)
+ finally:
+ self._lock.release()\n""" % (f, f))
--- /dev/null
+# Copyright (C) AB Strakt
+# Copyright (C) Jean-Paul Calderone
+# See LICENSE for details.
+
+"""
+pyOpenSSL - A simple wrapper around the OpenSSL library
+"""
+
+__all__ = [
+ "__author__", "__copyright__", "__email__", "__license__", "__summary__",
+ "__title__", "__uri__", "__version__",
+]
+
+__version__ = "19.0.0"
+
+__title__ = "pyOpenSSL"
+__uri__ = "https://pyopenssl.org/"
+__summary__ = "Python wrapper module around the OpenSSL library"
+__author__ = "The pyOpenSSL developers"
+__email__ = "cryptography-dev@python.org"
+__license__ = "Apache License, Version 2.0"
+__copyright__ = "Copyright 2001-2017 {0}".format(__author__)
--- /dev/null
+Metadata-Version: 2.1
+Name: pyOpenSSL
+Version: 19.0.0
+Summary: Python wrapper module around the OpenSSL library
+Home-page: https://pyopenssl.org/
+Author: The pyOpenSSL developers
+Author-email: cryptography-dev@python.org
+Maintainer: Hynek Schlawack
+Maintainer-email: hs@ox.cx
+License: Apache License, Version 2.0
+Description: ========================================================
+ pyOpenSSL -- A Python wrapper around the OpenSSL library
+ ========================================================
+
+ .. image:: https://readthedocs.org/projects/pyopenssl/badge/?version=stable
+ :target: https://pyopenssl.org/en/stable/
+ :alt: Stable Docs
+
+ .. image:: https://travis-ci.org/pyca/pyopenssl.svg?branch=master
+ :target: https://travis-ci.org/pyca/pyopenssl
+ :alt: Build status
+
+ .. image:: https://codecov.io/github/pyca/pyopenssl/branch/master/graph/badge.svg
+ :target: https://codecov.io/github/pyca/pyopenssl
+ :alt: Test coverage
+
+ **Note:** The Python Cryptographic Authority **strongly suggests** the use of `pyca/cryptography`_
+ where possible. If you are using pyOpenSSL for anything other than making a TLS connection
+ **you should move to cryptography and drop your pyOpenSSL dependency**.
+
+ 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.
+
+ You can find more information in the documentation_.
+ Development takes place on GitHub_.
+
+
+ Discussion
+ ==========
+
+ If you run into bugs, you can file them in our `issue tracker`_.
+
+ We maintain a cryptography-dev_ mailing list for both user and development discussions.
+
+ You can also join ``#cryptography-dev`` on Freenode to ask questions or get involved.
+
+
+ .. _documentation: https://pyopenssl.org/
+ .. _`issue tracker`: https://github.com/pyca/pyopenssl/issues
+ .. _cryptography-dev: https://mail.python.org/mailman/listinfo/cryptography-dev
+ .. _GitHub: https://github.com/pyca/pyopenssl
+ .. _`pyca/cryptography`: https://github.com/pyca/cryptography
+
+
+ Release Information
+ ===================
+
+ 19.0.0 (2019-01-21)
+ -------------------
+
+
+ Backward-incompatible changes:
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ - ``X509Store.add_cert`` no longer raises an error if you add a duplicate cert.
+ `#787 <https://github.com/pyca/pyopenssl/pull/787>`_
+
+
+ Deprecations:
+ ^^^^^^^^^^^^^
+
+ *none*
+
+
+ Changes:
+ ^^^^^^^^
+
+ - pyOpenSSL now works with OpenSSL 1.1.1.
+ `#805 <https://github.com/pyca/pyopenssl/pull/805>`_
+ - pyOpenSSL now handles NUL bytes in ``X509Name.get_components()``
+ `#804 <https://github.com/pyca/pyopenssl/pull/804>`_
+
+
+ `Full changelog <https://pyopenssl.org/en/stable/changelog.html>`_.
+
+
+Platform: UNKNOWN
+Classifier: Development Status :: 6 - Mature
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: POSIX
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Security :: Cryptography
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Networking
+Provides-Extra: docs
+Provides-Extra: test
--- /dev/null
+.coveragerc
+CHANGELOG.rst
+CODE_OF_CONDUCT.rst
+CONTRIBUTING.rst
+INSTALL.rst
+LICENSE
+MANIFEST.in
+README.rst
+setup.cfg
+setup.py
+tox.ini
+doc/ChangeLog_old.txt
+doc/Makefile
+doc/Quotes
+doc/README
+doc/api.rst
+doc/backward-compatibility.rst
+doc/changelog.rst
+doc/conf.py
+doc/index.rst
+doc/install.rst
+doc/internals.rst
+doc/introduction.rst
+doc/make.bat
+doc/api/crypto.rst
+doc/api/ssl.rst
+doc/images/pyopenssl-brand.png
+doc/images/pyopenssl-icon.png
+doc/images/pyopenssl-logo.png
+doc/images/pyopenssl.svg
+examples/README.rst
+examples/SecureXMLRPCServer.py
+examples/certgen.py
+examples/mk_simple_certs.py
+examples/proxy.py
+examples/simple/README
+examples/simple/client.py
+examples/simple/server.py
+examples/sni/README
+examples/sni/another.invalid.crt
+examples/sni/another.invalid.key
+examples/sni/client.py
+examples/sni/example.invalid.crt
+examples/sni/example.invalid.key
+examples/sni/server.py
+rpm/build_script
+src/OpenSSL/SSL.py
+src/OpenSSL/__init__.py
+src/OpenSSL/_util.py
+src/OpenSSL/crypto.py
+src/OpenSSL/debug.py
+src/OpenSSL/rand.py
+src/OpenSSL/tsafe.py
+src/OpenSSL/version.py
+src/pyOpenSSL.egg-info/PKG-INFO
+src/pyOpenSSL.egg-info/SOURCES.txt
+src/pyOpenSSL.egg-info/dependency_links.txt
+src/pyOpenSSL.egg-info/requires.txt
+src/pyOpenSSL.egg-info/top_level.txt
+tests/__init__.py
+tests/conftest.py
+tests/memdbg.py
+tests/test_crypto.py
+tests/test_debug.py
+tests/test_rand.py
+tests/test_ssl.py
+tests/test_tsafe.py
+tests/test_util.py
+tests/util.py
\ No newline at end of file
--- /dev/null
+cryptography>=2.3
+six>=1.5.2
+
+[docs]
+sphinx
+sphinx_rtd_theme
+
+[test]
+flaky
+pretend
+pytest>=3.0.1
--- /dev/null
+# Copyright (C) Jean-Paul Calderone
+# See LICENSE for details.
+
+"""
+Package containing unit tests for :py:mod:`OpenSSL`.
+"""
--- /dev/null
+# Copyright (c) The pyOpenSSL developers
+# See LICENSE for details.
+
+from tempfile import mktemp
+
+import pytest
+
+
+def pytest_report_header(config):
+ import OpenSSL.SSL
+ import cryptography
+
+ return "OpenSSL: {openssl}\ncryptography: {cryptography}".format(
+ openssl=OpenSSL.SSL.SSLeay_version(OpenSSL.SSL.SSLEAY_VERSION),
+ cryptography=cryptography.__version__
+ )
+
+
+@pytest.fixture
+def tmpfile(tmpdir):
+ """
+ Return UTF-8-encoded bytes of a path to a tmp file.
+
+ The file will be cleaned up after the test run.
+ """
+ return mktemp(dir=tmpdir.dirname).encode("utf-8")
--- /dev/null
+import sys
+
+import traceback
+
+from cffi import api as _api
+
+
+sys.modules['ssl'] = None
+sys.modules['_hashlib'] = None
+
+
+_ffi = _api.FFI()
+_ffi.cdef(
+ """
+ void *malloc(size_t size);
+ void free(void *ptr);
+ void *realloc(void *ptr, size_t size);
+
+ int CRYPTO_set_mem_functions(void *(*m)(size_t),void *(*r)(void *,size_t), void (*f)(void *));
+
+ int backtrace(void **buffer, int size);
+ char **backtrace_symbols(void *const *buffer, int size);
+ void backtrace_symbols_fd(void *const *buffer, int size, int fd);
+ """) # noqa
+_api = _ffi.verify(
+ """
+ #include <openssl/crypto.h>
+ #include <stdlib.h>
+ #include <execinfo.h>
+ """, libraries=["crypto"])
+C = _ffi.dlopen(None)
+
+verbose = False
+
+
+def log(s):
+ if verbose:
+ print(s)
+
+
+def _backtrace():
+ buf = _ffi.new("void*[]", 64)
+ result = _api.backtrace(buf, len(buf))
+ strings = _api.backtrace_symbols(buf, result)
+ stack = [_ffi.string(strings[i]) for i in range(result)]
+ C.free(strings)
+ return stack
+
+
+@_ffi.callback("void*(*)(size_t)")
+def malloc(n):
+ memory = C.malloc(n)
+ python_stack = traceback.extract_stack(limit=3)
+ c_stack = _backtrace()
+ heap[memory] = [(n, python_stack, c_stack)]
+ log("malloc(%d) -> %s" % (n, memory))
+ return memory
+
+
+@_ffi.callback("void*(*)(void*, size_t)")
+def realloc(p, n):
+ memory = C.realloc(p, n)
+ old = heap.pop(p)
+
+ python_stack = traceback.extract_stack(limit=3)
+ c_stack = _backtrace()
+
+ old.append((n, python_stack, c_stack))
+ heap[memory] = old
+ log("realloc(0x%x, %d) -> %s" % (int(_ffi.cast("int", p)), n, memory))
+ return memory
+
+
+@_ffi.callback("void(*)(void*)")
+def free(p):
+ if p != _ffi.NULL:
+ C.free(p)
+ del heap[p]
+ log("free(0x%x)" % (int(_ffi.cast("int", p)),))
+
+
+if _api.CRYPTO_set_mem_functions(malloc, realloc, free):
+ log('Enabled memory debugging')
+ heap = {}
+else:
+ log('Failed to enable memory debugging')
+ heap = None
--- /dev/null
+# Copyright (c) Jean-Paul Calderone
+# See LICENSE file for details.
+
+"""
+Unit tests for :py:mod:`OpenSSL.crypto`.
+"""
+
+from warnings import simplefilter
+
+import base64
+from subprocess import PIPE, Popen
+from datetime import datetime, timedelta
+
+import pytest
+
+from six import binary_type
+
+from cryptography import x509
+from cryptography.hazmat.backends.openssl.backend import backend
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import rsa
+
+import flaky
+
+from OpenSSL.crypto import TYPE_RSA, TYPE_DSA, Error, PKey, PKeyType
+from OpenSSL.crypto import X509, X509Type, X509Name, X509NameType
+from OpenSSL.crypto import (
+ X509Store,
+ X509StoreFlags,
+ X509StoreType,
+ X509StoreContext,
+ X509StoreContextError
+)
+from OpenSSL.crypto import X509Req, X509ReqType
+from OpenSSL.crypto import X509Extension, X509ExtensionType
+from OpenSSL.crypto import load_certificate, load_privatekey
+from OpenSSL.crypto import load_publickey, dump_publickey
+from OpenSSL.crypto import FILETYPE_PEM, FILETYPE_ASN1, FILETYPE_TEXT
+from OpenSSL.crypto import dump_certificate, load_certificate_request
+from OpenSSL.crypto import dump_certificate_request, dump_privatekey
+from OpenSSL.crypto import PKCS7, PKCS7Type, load_pkcs7_data
+from OpenSSL.crypto import PKCS12, PKCS12Type, load_pkcs12
+from OpenSSL.crypto import CRL, Revoked, dump_crl, load_crl
+from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType
+from OpenSSL.crypto import (
+ sign, verify, get_elliptic_curve, get_elliptic_curves)
+
+from .util import EqualityTestsMixin, is_consistent_type, WARNING_TYPE_EXPECTED
+
+
+def normalize_privatekey_pem(pem):
+ return dump_privatekey(FILETYPE_PEM, load_privatekey(FILETYPE_PEM, pem))
+
+
+GOOD_CIPHER = "blowfish"
+BAD_CIPHER = "zippers"
+
+GOOD_DIGEST = "SHA1"
+BAD_DIGEST = "monkeys"
+
+old_root_cert_pem = b"""-----BEGIN CERTIFICATE-----
+MIIC7TCCAlagAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
+BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU
+ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwIhgPMjAwOTAzMjUxMjM2
+NThaGA8yMDE3MDYxMTEyMzY1OFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklM
+MRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9U
+ZXN0aW5nIFJvb3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPmaQumL
+urpE527uSEHdL1pqcDRmWzu+98Y6YHzT/J7KWEamyMCNZ6fRW1JCR782UQ8a07fy
+2xXsKy4WdKaxyG8CcatwmXvpvRQ44dSANMihHELpANTdyVp6DCysED6wkQFurHlF
+1dshEaJw8b/ypDhmbVIo6Ci1xvCJqivbLFnbAgMBAAGjgbswgbgwHQYDVR0OBBYE
+FINVdy1eIfFJDAkk51QJEo3IfgSuMIGIBgNVHSMEgYAwfoAUg1V3LV4h8UkMCSTn
+VAkSjch+BK6hXKRaMFgxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UE
+BxMHQ2hpY2FnbzEQMA4GA1UEChMHVGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBS
+b290IENBggg9DMTgxt659DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GB
+AGGCDazMJGoWNBpc03u6+smc95dEead2KlZXBATOdFT1VesY3+nUOqZhEhTGlDMi
+hkgaZnzoIq/Uamidegk4hirsCT/R+6vsKAAxNTcBjUeZjlykCJWy5ojShGftXIKY
+w/njVbKMXrvc83qmTdGl3TAM0fxQIpqgcglFLveEBgzn
+-----END CERTIFICATE-----
+"""
+
+root_cert_pem = b"""-----BEGIN CERTIFICATE-----
+MIIC6TCCAlKgAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
+BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU
+ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMTcwNjExMjIzMjU5
+WhcNMzcwNjA2MjIzMjU5WjBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMCSUwxEDAO
+BgNVBAcTB0NoaWNhZ28xEDAOBgNVBAoTB1Rlc3RpbmcxGDAWBgNVBAMTD1Rlc3Rp
+bmcgUm9vdCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA+ZpC6Yu6ukTn
+bu5IQd0vWmpwNGZbO773xjpgfNP8nspYRqbIwI1np9FbUkJHvzZRDxrTt/LbFewr
+LhZ0prHIbwJxq3CZe+m9FDjh1IA0yKEcQukA1N3JWnoMLKwQPrCRAW6seUXV2yER
+onDxv/KkOGZtUijoKLXG8ImqK9ssWdsCAwEAAaOBuzCBuDAdBgNVHQ4EFgQUg1V3
+LV4h8UkMCSTnVAkSjch+BK4wgYgGA1UdIwSBgDB+gBSDVXctXiHxSQwJJOdUCRKN
+yH4ErqFcpFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdD
+aGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3Qg
+Q0GCCD0MxODG3rn0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEANFYQ
+R+T70VcZ+SnvURnwviFgCXeedBzCr21meo+DNHbkp2gudB9W8Xrned/wtUBVymy9
+gjB5jINfU7Lci0H57Evsw96UJJVfhXdUMHpqt1RGCoEd9FWnrDyrSy0NysnBT2bH
+lEqxh3aFEUx9IOQ4sgnx1/NOFXBpkRtivl6O0Ec=
+-----END CERTIFICATE-----
+"""
+
+root_key_pem = b"""-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQD5mkLpi7q6ROdu7khB3S9aanA0Zls7vvfGOmB80/yeylhGpsjA
+jWen0VtSQke/NlEPGtO38tsV7CsuFnSmschvAnGrcJl76b0UOOHUgDTIoRxC6QDU
+3claegwsrBA+sJEBbqx5RdXbIRGicPG/8qQ4Zm1SKOgotcbwiaor2yxZ2wIDAQAB
+AoGBAPCgMpmLxzwDaUmcFbTJUvlLW1hoxNNYSu2jIZm1k/hRAcE60JYwvBkgz3UB
+yMEh0AtLxYe0bFk6EHah11tMUPgscbCq73snJ++8koUw+csk22G65hOs51bVb7Aa
+6JBe67oLzdtvgCUFAA2qfrKzWRZzAdhUirQUZgySZk+Xq1pBAkEA/kZG0A6roTSM
+BVnx7LnPfsycKUsTumorpXiylZJjTi9XtmzxhrYN6wgZlDOOwOLgSQhszGpxVoMD
+u3gByT1b2QJBAPtL3mSKdvwRu/+40zaZLwvSJRxaj0mcE4BJOS6Oqs/hS1xRlrNk
+PpQ7WJ4yM6ZOLnXzm2mKyxm50Mv64109FtMCQQDOqS2KkjHaLowTGVxwC0DijMfr
+I9Lf8sSQk32J5VWCySWf5gGTfEnpmUa41gKTMJIbqZZLucNuDcOtzUaeWZlZAkA8
+ttXigLnCqR486JDPTi9ZscoZkZ+w7y6e/hH8t6d5Vjt48JVyfjPIaJY+km58LcN3
+6AWSeGAdtRFHVzR7oHjVAkB4hutvxiOeiIVQNBhM6RSI9aBPMI21DoX2JRoxvNW2
+cbvAhow217X9V0dVerEOKxnNYspXRrh36h7k4mQA+sDq
+-----END RSA PRIVATE KEY-----
+"""
+
+intermediate_cert_pem = b"""-----BEGIN CERTIFICATE-----
+MIICVzCCAcCgAwIBAgIRAMPzhm6//0Y/g2pmnHR2C4cwDQYJKoZIhvcNAQENBQAw
+WDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAw
+DgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMTQw
+ODI4MDIwNDA4WhcNMjQwODI1MDIwNDA4WjBmMRUwEwYDVQQDEwxpbnRlcm1lZGlh
+dGUxDDAKBgNVBAoTA29yZzERMA8GA1UECxMIb3JnLXVuaXQxCzAJBgNVBAYTAlVT
+MQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU2FuIERpZWdvMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDYcEQw5lfbEQRjr5Yy4yxAHGV0b9Al+Lmu7wLHMkZ/ZMmK
+FGIbljbviiD1Nz97Oh2cpB91YwOXOTN2vXHq26S+A5xe8z/QJbBsyghMur88CjdT
+21H2qwMa+r5dCQwEhuGIiZ3KbzB/n4DTMYI5zy4IYPv0pjxShZn4aZTCCK2IUwID
+AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAPIWSkLX
+QRMApOjjyC+tMxumT5e2pMqChHmxobQK4NMdrf2VCx+cRT6EmY8sK3/Xl/X8UBQ+
+9n5zXb1ZwhW/sTWgUvmOceJ4/XVs9FkdWOOn1J0XBch9ZIiFe/s5ASIgG7fUdcUF
+9mAWS6FK2ca3xIh5kIupCXOFa0dPvlw/YUFT
+-----END CERTIFICATE-----
+"""
+
+intermediate_key_pem = b"""-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDYcEQw5lfbEQRjr5Yy4yxAHGV0b9Al+Lmu7wLHMkZ/ZMmKFGIb
+ljbviiD1Nz97Oh2cpB91YwOXOTN2vXHq26S+A5xe8z/QJbBsyghMur88CjdT21H2
+qwMa+r5dCQwEhuGIiZ3KbzB/n4DTMYI5zy4IYPv0pjxShZn4aZTCCK2IUwIDAQAB
+AoGAfSZVV80pSeOKHTYfbGdNY/jHdU9eFUa/33YWriXU+77EhpIItJjkRRgivIfo
+rhFJpBSGmDLblaqepm8emsXMeH4+2QzOYIf0QGGP6E6scjTt1PLqdqKfVJ1a2REN
+147cujNcmFJb/5VQHHMpaPTgttEjlzuww4+BCDPsVRABWrkCQQD3loH36nLoQTtf
++kQq0T6Bs9/UWkTAGo0ND81ALj0F8Ie1oeZg6RNT96RxZ3aVuFTESTv6/TbjWywO
+wdzlmV1vAkEA38rTJ6PTwaJlw5OttdDzAXGPB9tDmzh9oSi7cHwQQXizYd8MBYx4
+sjHUKD3dCQnb1dxJFhd3BT5HsnkRMbVZXQJAbXduH17ZTzcIOXc9jHDXYiFVZV5D
+52vV0WCbLzVCZc3jMrtSUKa8lPN5EWrdU3UchWybyG0MR5mX8S5lrF4SoQJAIyUD
+DBKaSqpqONCUUx1BTFS9FYrFjzbL4+c1qHCTTPTblt8kUCrDOZjBrKAqeiTmNSum
+/qUot9YUBF8m6BuGsQJATHHmdFy/fG1VLkyBp49CAa8tN3Z5r/CgTznI4DfMTf4C
+NbRHn2UmYlwQBa+L5lg9phewNe8aEwpPyPLoV85U8Q==
+-----END RSA PRIVATE KEY-----
+"""
+
+server_cert_pem = b"""-----BEGIN CERTIFICATE-----
+MIICJDCCAY2gAwIBAgIJAJn/HpR21r/8MA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJJTDEQMA4GA1UEBwwHQ2hpY2FnbzEQMA4GA1UECgwH
+VGVzdGluZzEYMBYGA1UEAwwPVGVzdGluZyBSb290IENBMB4XDTE3MDYxMjAwMTA1
+N1oXDTM3MDYwNzAwMTA1N1owGDEWMBQGA1UEAwwNbG92ZWx5IHNlcnZlcjCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvqb4brndXS2kEL84qXbZXE6LYK+UrhNi
+70sdIM/24NVN7tXkPlOXqrMWhFHHml+aeSpPkH5b1vKnY1TcULmEubnNICtvjmZ5
+SGMQn+J+RmBs1SMd0EgY/0wBBQdlrlYp2QYgm8YC+zxTNSqWvhMFZAgHbj6Un5SS
+T8JGBqytjB0CAwEAAaM2MDQwHQYDVR0OBBYEFINVdy1eIfFJDAkk51QJEo3IfgSu
+MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBBQUAA4GBAGki1K6WgHHJ
+qC6aY2EowjaWOXLO6jUZIhGk7BA7vMRfNug429AOZ4m5F6OQhzmJmlw67Jyu2FeI
+h0VtBuQoHPtjqZXF59oX6hMMmGLMs9pV0UA3fJs5MYA4/V5ZcQy0Ie0QoJNejLzE
+6V1Qz1rRTYLUyEcpI7ZCmBg2KQQI8YZI
+-----END CERTIFICATE-----
+"""
+
+server_key_pem = normalize_privatekey_pem(b"""-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQC+pvhuud1dLaQQvzipdtlcTotgr5SuE2LvSx0gz/bg1U3u1eQ+
+U5eqsxaEUceaX5p5Kk+QflvW8qdjVNxQuYS5uc0gK2+OZnlIYxCf4n5GYGzVIx3Q
+SBj/TAEFB2WuVinZBiCbxgL7PFM1Kpa+EwVkCAduPpSflJJPwkYGrK2MHQIDAQAB
+AoGAbwuZ0AR6JveahBaczjfnSpiFHf+mve2UxoQdpyr6ROJ4zg/PLW5K/KXrC48G
+j6f3tXMrfKHcpEoZrQWUfYBRCUsGD5DCazEhD8zlxEHahIsqpwA0WWssJA2VOLEN
+j6DuV2pCFbw67rfTBkTSo32ahfXxEKev5KswZk0JIzH3ooECQQDgzS9AI89h0gs8
+Dt+1m11Rzqo3vZML7ZIyGApUzVan+a7hbc33nbGRkAXjHaUBJO31it/H6dTO+uwX
+msWwNG5ZAkEA2RyFKs5xR5USTFaKLWCgpH/ydV96KPOpBND7TKQx62snDenFNNbn
+FwwOhpahld+vqhYk+pfuWWUpQciE+Bu7ZQJASjfT4sQv4qbbKK/scePicnDdx9th
+4e1EeB9xwb+tXXXUo/6Bor/AcUNwfiQ6Zt9PZOK9sR3lMZSsP7rMi7kzuQJABie6
+1sXXjFH7nNJvRG4S39cIxq8YRYTy68II/dlB2QzGpKxV/POCxbJ/zu0CU79tuYK7
+NaeNCFfH3aeTrX0LyQJAMBWjWmeKM2G2sCExheeQK0ROnaBC8itCECD4Jsve4nqf
+r50+LF74iLXFwqysVCebPKMOpDWp/qQ1BbJQIPs7/A==
+-----END RSA PRIVATE KEY-----
+""")
+
+intermediate_server_cert_pem = b"""-----BEGIN CERTIFICATE-----
+MIICWDCCAcGgAwIBAgIRAPQFY9jfskSihdiNSNdt6GswDQYJKoZIhvcNAQENBQAw
+ZjEVMBMGA1UEAxMMaW50ZXJtZWRpYXRlMQwwCgYDVQQKEwNvcmcxETAPBgNVBAsT
+CG9yZy11bml0MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVNh
+biBEaWVnbzAeFw0xNDA4MjgwMjEwNDhaFw0yNDA4MjUwMjEwNDhaMG4xHTAbBgNV
+BAMTFGludGVybWVkaWF0ZS1zZXJ2aWNlMQwwCgYDVQQKEwNvcmcxETAPBgNVBAsT
+CG9yZy11bml0MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVNh
+biBEaWVnbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqpJZygd+w1faLOr1
+iOAmbBhx5SZWcTCZ/ZjHQTJM7GuPT624QkqsixFghRKdDROwpwnAP7gMRukLqiy4
++kRuGT5OfyGggL95i2xqA+zehjj08lSTlvGHpePJgCyTavIy5+Ljsj4DKnKyuhxm
+biXTRrH83NDgixVkObTEmh/OVK0CAwEAATANBgkqhkiG9w0BAQ0FAAOBgQBa0Npw
+UkzjaYEo1OUE1sTI6Mm4riTIHMak4/nswKh9hYup//WVOlr/RBSBtZ7Q/BwbjobN
+3bfAtV7eSAqBsfxYXyof7G1ALANQERkq3+oyLP1iVt08W1WOUlIMPhdCF/QuCwy6
+x9MJLhUCGLJPM+O2rAPWVD9wCmvq10ALsiH3yA==
+-----END CERTIFICATE-----
+"""
+
+intermediate_server_key_pem = b"""-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCqklnKB37DV9os6vWI4CZsGHHlJlZxMJn9mMdBMkzsa49PrbhC
+SqyLEWCFEp0NE7CnCcA/uAxG6QuqLLj6RG4ZPk5/IaCAv3mLbGoD7N6GOPTyVJOW
+8Yel48mALJNq8jLn4uOyPgMqcrK6HGZuJdNGsfzc0OCLFWQ5tMSaH85UrQIDAQAB
+AoGAIQ594j5zna3/9WaPsTgnmhlesVctt4AAx/n827DA4ayyuHFlXUuVhtoWR5Pk
+5ezj9mtYW8DyeCegABnsu2vZni/CdvU6uiS1Hv6qM1GyYDm9KWgovIP9rQCDSGaz
+d57IWVGxx7ODFkm3gN5nxnSBOFVHytuW1J7FBRnEsehRroECQQDXHFOv82JuXDcz
+z3+4c74IEURdOHcbycxlppmK9kFqm5lsUdydnnGW+mvwDk0APOB7Wg7vyFyr393e
+dpmBDCzNAkEAyv6tVbTKUYhSjW+QhabJo896/EqQEYUmtMXxk4cQnKeR/Ao84Rkf
+EqD5IykMUfUI0jJU4DGX+gWZ10a7kNbHYQJAVFCuHNFxS4Cpwo0aqtnzKoZaHY/8
+X9ABZfafSHCtw3Op92M+7ikkrOELXdS9KdKyyqbKJAKNEHF3LbOfB44WIQJAA2N4
+9UNNVUsXRbElEnYUS529CdUczo4QdVgQjkvk5RiPAUwSdBd9Q0xYnFOlFwEmIowg
+ipWJWe0aAlP18ZcEQQJBAL+5lekZ/GUdQoZ4HAsN5a9syrzavJ9VvU1KOOPorPZK
+nMRZbbQgP+aSB7yl6K0gaLaZ8XaK0pjxNBh6ASqg9f4=
+-----END RSA PRIVATE KEY-----
+"""
+
+client_cert_pem = b"""-----BEGIN CERTIFICATE-----
+MIICIjCCAYugAwIBAgIJAKxpFI5lODkjMA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJJTDEQMA4GA1UEBwwHQ2hpY2FnbzEQMA4GA1UECgwH
+VGVzdGluZzEYMBYGA1UEAwwPVGVzdGluZyBSb290IENBMB4XDTE3MDYxMjAwMDQx
+M1oXDTM3MDYwNzAwMDQxM1owFjEUMBIGA1UEAwwLdWdseSBjbGllbnQwgZ8wDQYJ
+KoZIhvcNAQEBBQADgY0AMIGJAoGBAMBmH9JG02bme0xPipvpjMSlOugyWrauf4at
+EdGJn7GQLD8IY2Fu0+Kvv9DFpSPboFKZCsfDVsYoRs+xaJbtt1dJ6ymX7EqKS7gb
+8q+eeZZ14keqyJd5Rm2q6swQtw9ADD3E8cS6GqpQm+8SgxOycISoYz7sO1ugJFqN
+jId+W4BFAgMBAAGjNjA0MB0GA1UdDgQWBBSDVXctXiHxSQwJJOdUCRKNyH4ErjAT
+BgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOBgQAMqcHyweaCOZNN
+dWQQOsBKQlL5wqVVZwucHPWqobjxpULKy9gS2ha2zbgkXcB/BnBOSwe0Fm+MJV0T
+NbnTghcGJNpEH7VKn4xSLvIGZmnZZWgxeIB16z4GhpkK2fShBJ+6GKZjsgjT0lSH
+JRgjHbWutZfZvbSHXr9n7PIphG1Ojg==
+-----END CERTIFICATE-----
+"""
+
+client_key_pem = normalize_privatekey_pem(b"""-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDAZh/SRtNm5ntMT4qb6YzEpTroMlq2rn+GrRHRiZ+xkCw/CGNh
+btPir7/QxaUj26BSmQrHw1bGKEbPsWiW7bdXSespl+xKiku4G/KvnnmWdeJHqsiX
+eUZtqurMELcPQAw9xPHEuhqqUJvvEoMTsnCEqGM+7DtboCRajYyHfluARQIDAQAB
+AoGATkZ+NceY5Glqyl4mD06SdcKfV65814vg2EL7V9t8+/mi9rYL8KztSXGlQWPX
+zuHgtRoMl78yQ4ZJYOBVo+nsx8KZNRCEBlE19bamSbQLCeQMenWnpeYyQUZ908gF
+h6L9qsFVJepgA9RDgAjyDoS5CaWCdCCPCH2lDkdcqC54SVUCQQDseuduc4wi8h4t
+V8AahUn9fn9gYfhoNuM0gdguTA0nPLVWz4hy1yJiWYQe0H7NLNNTmCKiLQaJpAbb
+TC6vE8C7AkEA0Ee8CMJUc20BnGEmxwgWcVuqFWaKCo8jTH1X38FlATUsyR3krjW2
+dL3yDD9NwHxsYP7nTKp/U8MV7U9IBn4y/wJBAJl7H0/BcLeRmuJk7IqJ7b635iYB
+D/9beFUw3MUXmQXZUfyYz39xf6CDZsu1GEdEC5haykeln3Of4M9d/4Kj+FcCQQCY
+si6xwT7GzMDkk/ko684AV3KPc/h6G0yGtFIrMg7J3uExpR/VdH2KgwMkZXisSMvw
+JJEQjOMCVsEJlRk54WWjAkEAzoZNH6UhDdBK5F38rVt/y4SEHgbSfJHIAmPS32Kq
+f6GGcfNpip0Uk7q7udTKuX7Q/buZi/C4YW7u3VKAquv9NA==
+-----END RSA PRIVATE KEY-----
+""")
+
+cleartextCertificatePEM = b"""-----BEGIN CERTIFICATE-----
+MIIC6TCCAlKgAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
+BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU
+ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwHhcNMTcwNjExMjIzMjU5
+WhcNMzcwNjA2MjIzMjU5WjBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMCSUwxEDAO
+BgNVBAcTB0NoaWNhZ28xEDAOBgNVBAoTB1Rlc3RpbmcxGDAWBgNVBAMTD1Rlc3Rp
+bmcgUm9vdCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA+ZpC6Yu6ukTn
+bu5IQd0vWmpwNGZbO773xjpgfNP8nspYRqbIwI1np9FbUkJHvzZRDxrTt/LbFewr
+LhZ0prHIbwJxq3CZe+m9FDjh1IA0yKEcQukA1N3JWnoMLKwQPrCRAW6seUXV2yER
+onDxv/KkOGZtUijoKLXG8ImqK9ssWdsCAwEAAaOBuzCBuDAdBgNVHQ4EFgQUg1V3
+LV4h8UkMCSTnVAkSjch+BK4wgYgGA1UdIwSBgDB+gBSDVXctXiHxSQwJJOdUCRKN
+yH4ErqFcpFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdD
+aGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3Qg
+Q0GCCD0MxODG3rn0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEANFYQ
+R+T70VcZ+SnvURnwviFgCXeedBzCr21meo+DNHbkp2gudB9W8Xrned/wtUBVymy9
+gjB5jINfU7Lci0H57Evsw96UJJVfhXdUMHpqt1RGCoEd9FWnrDyrSy0NysnBT2bH
+lEqxh3aFEUx9IOQ4sgnx1/NOFXBpkRtivl6O0Ec=
+-----END CERTIFICATE-----
+"""
+
+cleartextPrivateKeyPEM = normalize_privatekey_pem(b"""\
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQD5mkLpi7q6ROdu7khB3S9aanA0Zls7vvfGOmB80/yeylhGpsjA
+jWen0VtSQke/NlEPGtO38tsV7CsuFnSmschvAnGrcJl76b0UOOHUgDTIoRxC6QDU
+3claegwsrBA+sJEBbqx5RdXbIRGicPG/8qQ4Zm1SKOgotcbwiaor2yxZ2wIDAQAB
+AoGBAPCgMpmLxzwDaUmcFbTJUvlLW1hoxNNYSu2jIZm1k/hRAcE60JYwvBkgz3UB
+yMEh0AtLxYe0bFk6EHah11tMUPgscbCq73snJ++8koUw+csk22G65hOs51bVb7Aa
+6JBe67oLzdtvgCUFAA2qfrKzWRZzAdhUirQUZgySZk+Xq1pBAkEA/kZG0A6roTSM
+BVnx7LnPfsycKUsTumorpXiylZJjTi9XtmzxhrYN6wgZlDOOwOLgSQhszGpxVoMD
+u3gByT1b2QJBAPtL3mSKdvwRu/+40zaZLwvSJRxaj0mcE4BJOS6Oqs/hS1xRlrNk
+PpQ7WJ4yM6ZOLnXzm2mKyxm50Mv64109FtMCQQDOqS2KkjHaLowTGVxwC0DijMfr
+I9Lf8sSQk32J5VWCySWf5gGTfEnpmUa41gKTMJIbqZZLucNuDcOtzUaeWZlZAkA8
+ttXigLnCqR486JDPTi9ZscoZkZ+w7y6e/hH8t6d5Vjt48JVyfjPIaJY+km58LcN3
+6AWSeGAdtRFHVzR7oHjVAkB4hutvxiOeiIVQNBhM6RSI9aBPMI21DoX2JRoxvNW2
+cbvAhow217X9V0dVerEOKxnNYspXRrh36h7k4mQA+sDq
+-----END RSA PRIVATE KEY-----
+""")
+
+cleartextCertificateRequestPEM = b"""-----BEGIN CERTIFICATE REQUEST-----
+MIIBnjCCAQcCAQAwXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQH
+EwdDaGljYWdvMRcwFQYDVQQKEw5NeSBDb21wYW55IEx0ZDEXMBUGA1UEAxMORnJl
+ZGVyaWNrIERlYW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANp6Y17WzKSw
+BsUWkXdqg6tnXy8H8hA1msCMWpc+/2KJ4mbv5NyD6UD+/SqagQqulPbF/DFea9nA
+E0zhmHJELcM8gUTIlXv/cgDWnmK4xj8YkjVUiCdqKRAKeuzLG1pGmwwF5lGeJpXN
+xQn5ecR0UYSOWj6TTGXB9VyUMQzCClcBAgMBAAGgADANBgkqhkiG9w0BAQUFAAOB
+gQAAJGuF/R/GGbeC7FbFW+aJgr9ee0Xbl6nlhu7pTe67k+iiKT2dsl2ti68MVTnu
+Vrb3HUNqOkiwsJf6kCtq5oPn3QVYzTa76Dt2y3Rtzv6boRSlmlfrgS92GNma8JfR
+oICQk3nAudi6zl1Dix3BCv1pUp5KMtGn3MeDEi6QFGy2rA==
+-----END CERTIFICATE REQUEST-----
+"""
+
+encryptedPrivateKeyPEM = b"""-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,9573604A18579E9E
+
+SHOho56WxDkT0ht10UTeKc0F5u8cqIa01kzFAmETw0MAs8ezYtK15NPdCXUm3X/2
+a17G7LSF5bkxOgZ7vpXyMzun/owrj7CzvLxyncyEFZWvtvzaAhPhvTJtTIB3kf8B
+8+qRcpTGK7NgXEgYBW5bj1y4qZkD4zCL9o9NQzsKI3Ie8i0239jsDOWR38AxjXBH
+mGwAQ4Z6ZN5dnmM4fhMIWsmFf19sNyAML4gHenQCHhmXbjXeVq47aC2ProInJbrm
++00TcisbAQ40V9aehVbcDKtS4ZbMVDwncAjpXpcncC54G76N6j7F7wL7L/FuXa3A
+fvSVy9n2VfF/pJ3kYSflLHH2G/DFxjF7dl0GxhKPxJjp3IJi9VtuvmN9R2jZWLQF
+tfC8dXgy/P9CfFQhlinqBTEwgH0oZ/d4k4NVFDSdEMaSdmBAjlHpc+Vfdty3HVnV
+rKXj//wslsFNm9kIwJGIgKUa/n2jsOiydrsk1mgH7SmNCb3YHgZhbbnq0qLat/HC
+gHDt3FHpNQ31QzzL3yrenFB2L9osIsnRsDTPFNi4RX4SpDgNroxOQmyzCCV6H+d4
+o1mcnNiZSdxLZxVKccq0AfRpHqpPAFnJcQHP6xyT9MZp6fBa0XkxDnt9kNU8H3Qw
+7SJWZ69VXjBUzMlQViLuaWMgTnL+ZVyFZf9hTF7U/ef4HMLMAVNdiaGG+G+AjCV/
+MbzjS007Oe4qqBnCWaFPSnJX6uLApeTbqAxAeyCql56ULW5x6vDMNC3dwjvS/CEh
+11n8RkgFIQA0AhuKSIg3CbuartRsJnWOLwgLTzsrKYL4yRog1RJrtw==
+-----END RSA PRIVATE KEY-----
+"""
+
+encryptedPrivateKeyPEMPassphrase = b"foobar"
+
+
+cleartextPublicKeyPEM = b"""-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxszlc+b71LvlLS0ypt/l
+gT/JzSVJtnEqw9WUNGeiChywX2mmQLHEt7KP0JikqUFZOtPclNY823Q4pErMTSWC
+90qlUxI47vNJbXGRfmO2q6Zfw6SE+E9iUb74xezbOJLjBuUIkQzEKEFV+8taiRV+
+ceg1v01yCT2+OjhQW3cxG42zxyRFmqesbQAUWgS3uhPrUQqYQUEiTmVhh4FBUKZ5
+XIneGUpX1S7mXRxTLH6YzRoGFqRoc9A0BBNcoXHTWnxV215k4TeHMFYE5RG0KYAS
+8Xk5iKICEXwnZreIt3jyygqoOKsKZMK/Zl2VhMGhJR6HXRpQCyASzEG7bgtROLhL
+ywIDAQAB
+-----END PUBLIC KEY-----
+"""
+
+# Some PKCS#7 stuff. Generated with the openssl command line:
+#
+# openssl crl2pkcs7 -inform pem -outform pem -certfile s.pem -nocrl
+#
+# with a certificate and key (but the key should be irrelevant) in s.pem
+pkcs7Data = b"""\
+-----BEGIN PKCS7-----
+MIIDNwYJKoZIhvcNAQcCoIIDKDCCAyQCAQExADALBgkqhkiG9w0BBwGgggMKMIID
+BjCCAm+gAwIBAgIBATANBgkqhkiG9w0BAQQFADB7MQswCQYDVQQGEwJTRzERMA8G
+A1UEChMITTJDcnlwdG8xFDASBgNVBAsTC00yQ3J5cHRvIENBMSQwIgYDVQQDExtN
+MkNyeXB0byBDZXJ0aWZpY2F0ZSBNYXN0ZXIxHTAbBgkqhkiG9w0BCQEWDm5ncHNA
+cG9zdDEuY29tMB4XDTAwMDkxMDA5NTEzMFoXDTAyMDkxMDA5NTEzMFowUzELMAkG
+A1UEBhMCU0cxETAPBgNVBAoTCE0yQ3J5cHRvMRIwEAYDVQQDEwlsb2NhbGhvc3Qx
+HTAbBgkqhkiG9w0BCQEWDm5ncHNAcG9zdDEuY29tMFwwDQYJKoZIhvcNAQEBBQAD
+SwAwSAJBAKy+e3dulvXzV7zoTZWc5TzgApr8DmeQHTYC8ydfzH7EECe4R1Xh5kwI
+zOuuFfn178FBiS84gngaNcrFi0Z5fAkCAwEAAaOCAQQwggEAMAkGA1UdEwQCMAAw
+LAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0G
+A1UdDgQWBBTPhIKSvnsmYsBVNWjj0m3M2z0qVTCBpQYDVR0jBIGdMIGagBT7hyNp
+65w6kxXlxb8pUU/+7Sg4AaF/pH0wezELMAkGA1UEBhMCU0cxETAPBgNVBAoTCE0y
+Q3J5cHRvMRQwEgYDVQQLEwtNMkNyeXB0byBDQTEkMCIGA1UEAxMbTTJDcnlwdG8g
+Q2VydGlmaWNhdGUgTWFzdGVyMR0wGwYJKoZIhvcNAQkBFg5uZ3BzQHBvc3QxLmNv
+bYIBADANBgkqhkiG9w0BAQQFAAOBgQA7/CqT6PoHycTdhEStWNZde7M/2Yc6BoJu
+VwnW8YxGO8Sn6UJ4FeffZNcYZddSDKosw8LtPOeWoK3JINjAk5jiPQ2cww++7QGG
+/g5NDjxFZNDJP1dGiLAxPW6JXwov4v0FmdzfLOZ01jDcgQQZqEpYlgpuI5JEWUQ9
+Ho4EzbYCOaEAMQA=
+-----END PKCS7-----
+"""
+
+pkcs7DataASN1 = base64.b64decode(b"""
+MIIDNwYJKoZIhvcNAQcCoIIDKDCCAyQCAQExADALBgkqhkiG9w0BBwGgggMKMIID
+BjCCAm+gAwIBAgIBATANBgkqhkiG9w0BAQQFADB7MQswCQYDVQQGEwJTRzERMA8G
+A1UEChMITTJDcnlwdG8xFDASBgNVBAsTC00yQ3J5cHRvIENBMSQwIgYDVQQDExtN
+MkNyeXB0byBDZXJ0aWZpY2F0ZSBNYXN0ZXIxHTAbBgkqhkiG9w0BCQEWDm5ncHNA
+cG9zdDEuY29tMB4XDTAwMDkxMDA5NTEzMFoXDTAyMDkxMDA5NTEzMFowUzELMAkG
+A1UEBhMCU0cxETAPBgNVBAoTCE0yQ3J5cHRvMRIwEAYDVQQDEwlsb2NhbGhvc3Qx
+HTAbBgkqhkiG9w0BCQEWDm5ncHNAcG9zdDEuY29tMFwwDQYJKoZIhvcNAQEBBQAD
+SwAwSAJBAKy+e3dulvXzV7zoTZWc5TzgApr8DmeQHTYC8ydfzH7EECe4R1Xh5kwI
+zOuuFfn178FBiS84gngaNcrFi0Z5fAkCAwEAAaOCAQQwggEAMAkGA1UdEwQCMAAw
+LAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0G
+A1UdDgQWBBTPhIKSvnsmYsBVNWjj0m3M2z0qVTCBpQYDVR0jBIGdMIGagBT7hyNp
+65w6kxXlxb8pUU/+7Sg4AaF/pH0wezELMAkGA1UEBhMCU0cxETAPBgNVBAoTCE0y
+Q3J5cHRvMRQwEgYDVQQLEwtNMkNyeXB0byBDQTEkMCIGA1UEAxMbTTJDcnlwdG8g
+Q2VydGlmaWNhdGUgTWFzdGVyMR0wGwYJKoZIhvcNAQkBFg5uZ3BzQHBvc3QxLmNv
+bYIBADANBgkqhkiG9w0BAQQFAAOBgQA7/CqT6PoHycTdhEStWNZde7M/2Yc6BoJu
+VwnW8YxGO8Sn6UJ4FeffZNcYZddSDKosw8LtPOeWoK3JINjAk5jiPQ2cww++7QGG
+/g5NDjxFZNDJP1dGiLAxPW6JXwov4v0FmdzfLOZ01jDcgQQZqEpYlgpuI5JEWUQ9
+Ho4EzbYCOaEAMQA=
+""")
+
+crlData = b"""\
+-----BEGIN X509 CRL-----
+MIIBWzCBxTANBgkqhkiG9w0BAQQFADBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMC
+SUwxEDAOBgNVBAcTB0NoaWNhZ28xEDAOBgNVBAoTB1Rlc3RpbmcxGDAWBgNVBAMT
+D1Rlc3RpbmcgUm9vdCBDQRcNMDkwNzI2MDQzNDU2WhcNMTIwOTI3MDI0MTUyWjA8
+MBUCAgOrGA8yMDA5MDcyNTIzMzQ1NlowIwICAQAYDzIwMDkwNzI1MjMzNDU2WjAM
+MAoGA1UdFQQDCgEEMA0GCSqGSIb3DQEBBAUAA4GBAEBt7xTs2htdD3d4ErrcGAw1
+4dKcVnIWTutoI7xxen26Wwvh8VCsT7i/UeP+rBl9rC/kfjWjzQk3/zleaarGTpBT
+0yp4HXRFFoRhhSE/hP+eteaPXRgrsNRLHe9ZDd69wmh7J1wMDb0m81RG7kqcbsid
+vrzEeLDRiiPl92dyyWmu
+-----END X509 CRL-----
+"""
+
+crlDataUnsupportedExtension = b"""\
+-----BEGIN X509 CRL-----
+MIIGRzCCBS8CAQIwDQYJKoZIhvcNAQELBQAwJzELMAkGA1UEBhMCVVMxGDAWBgNV
+BAMMD2NyeXB0b2dyYXBoeS5pbxgPMjAxNTAxMDEwMDAwMDBaGA8yMDE2MDEwMTAw
+MDAwMFowggTOMBQCAQAYDzIwMTUwMTAxMDAwMDAwWjByAgEBGA8yMDE1MDEwMTAw
+MDAwMFowXDAYBgNVHRgEERgPMjAxNTAxMDEwMDAwMDBaMDQGA1UdHQQtMCukKTAn
+MQswCQYDVQQGEwJVUzEYMBYGA1UEAwwPY3J5cHRvZ3JhcGh5LmlvMAoGA1UdFQQD
+CgEAMHICAQIYDzIwMTUwMTAxMDAwMDAwWjBcMBgGA1UdGAQRGA8yMDE1MDEwMTAw
+MDAwMFowNAYDVR0dBC0wK6QpMCcxCzAJBgNVBAYTAlVTMRgwFgYDVQQDDA9jcnlw
+dG9ncmFwaHkuaW8wCgYDVR0VBAMKAQEwcgIBAxgPMjAxNTAxMDEwMDAwMDBaMFww
+GAYDVR0YBBEYDzIwMTUwMTAxMDAwMDAwWjA0BgNVHR0ELTArpCkwJzELMAkGA1UE
+BhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeS5pbzAKBgNVHRUEAwoBAjByAgEE
+GA8yMDE1MDEwMTAwMDAwMFowXDAYBgNVHRgEERgPMjAxNTAxMDEwMDAwMDBaMDQG
+A1UdHQQtMCukKTAnMQswCQYDVQQGEwJVUzEYMBYGA1UEAwwPY3J5cHRvZ3JhcGh5
+LmlvMAoGA1UdFQQDCgEDMHICAQUYDzIwMTUwMTAxMDAwMDAwWjBcMBgGA1UdGAQR
+GA8yMDE1MDEwMTAwMDAwMFowNAYDVR0dBC0wK6QpMCcxCzAJBgNVBAYTAlVTMRgw
+FgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wCgYDVR0VBAMKAQQwcgIBBhgPMjAxNTAx
+MDEwMDAwMDBaMFwwGAYDVR0YBBEYDzIwMTUwMTAxMDAwMDAwWjA0BgNVHR0ELTAr
+pCkwJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeS5pbzAKBgNV
+HRUEAwoBBTByAgEHGA8yMDE1MDEwMTAwMDAwMFowXDAYBgNVHRgEERgPMjAxNTAx
+MDEwMDAwMDBaMDQGA1UdHQQtMCukKTAnMQswCQYDVQQGEwJVUzEYMBYGA1UEAwwP
+Y3J5cHRvZ3JhcGh5LmlvMAoGA1UdFQQDCgEGMHICAQgYDzIwMTUwMTAxMDAwMDAw
+WjBcMBgGA1UdGAQRGA8yMDE1MDEwMTAwMDAwMFowNAYDVR0dBC0wK6QpMCcxCzAJ
+BgNVBAYTAlVTMRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wCgYDVR0VBAMKAQgw
+cgIBCRgPMjAxNTAxMDEwMDAwMDBaMFwwGAYDVR0YBBEYDzIwMTUwMTAxMDAwMDAw
+WjA0BgNVHR0ELTArpCkwJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dy
+YXBoeS5pbzAKBgNVHRUEAwoBCTByAgEKGA8yMDE1MDEwMTAwMDAwMFowXDAYBgNV
+HRgEERgPMjAxNTAxMDEwMDAwMDBaMDQGA1UdHQQtMCukKTAnMQswCQYDVQQGEwJV
+UzEYMBYGA1UEAwwPY3J5cHRvZ3JhcGh5LmlvMAoGA1UdFQQDCgEKMC4CAQsYDzIw
+MTUwMTAxMDAwMDAwWjAYMAoGA1UdFQQDCgEBMAoGAyoDBAQDCgEAMA0GCSqGSIb3
+DQEBCwUAA4IBAQBTaloHlPaCZzYee8LxkWej5meiqxQVNWFoVdjesroa+f1FRrH+
+drRU60Nq97KCKf7f9GNN/J3ZIlQmYhmuDqh12f+XLpotoj1ZRfBz2hjFCkJlv+2c
+oWWGNHgA70ndFoVtcmX088SYpX8E3ARATivS4q2h9WlwV6rO93mhg3HGIe3JpcK4
+7BcW6Poi/ut/zsDOkVbI00SqaujRpdmdCTht82MH3ztjyDkI9KYaD/YEweKSrWOz
+SdEILd164bfBeLuplVI+xpmTEMVNpXBlSXl7+xIw9Vk7p7Q1Pa3k/SvhOldYCm6y
+C1xAg/AAq6w78yzYt18j5Mj0s6eeHi1YpHKw
+-----END X509 CRL-----
+"""
+
+
+# A broken RSA private key which can be used to test the error path through
+# PKey.check.
+inconsistentPrivateKeyPEM = b"""-----BEGIN RSA PRIVATE KEY-----
+MIIBPAIBAAJBAKy+e3dulvXzV7zoTZWc5TzgApr8DmeQHTYC8ydfzH7EECe4R1Xh
+5kwIzOuuFfn178FBiS84gngaNcrFi0Z5fAkCAwEaAQJBAIqm/bz4NA1H++Vx5Ewx
+OcKp3w19QSaZAwlGRtsUxrP7436QjnREM3Bm8ygU11BjkPVmtrKm6AayQfCHqJoT
+zIECIQDW0BoMoL0HOYM/mrTLhaykYAVqgIeJsPjvkEhTFXWBuQIhAM3deFAvWNu4
+nklUQ37XsCT2c9tmNt1LAT+slG2JOTTRAiAuXDtC/m3NYVwyHfFm+zKHRzHkClk2
+HjubeEgjpj32AQIhAJqMGTaZVOwevTXvvHwNeH+vRWsAYU/gbx+OQB+7VOcBAiEA
+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-----"""
+
+large_key_pem = b"""-----BEGIN RSA PRIVATE KEY-----
+MIIJYgIBAAKCAg4AtRua8eIeevRfsj+fkcHr1vmse7Kgb+oX1ssJAvCb1R7JQMnH
+hNDjDP6b3vEkZuPUzlDHymP+cNkXvvi4wJ4miVbO3+SeU4Sh+jmsHeHzGIXat9xW
+9PFtuPM5FQq8zvkY8aDeRYmYwN9JKu4/neMBCBqostYlTEWg+bSytO/qWnyHTHKh
+g0GfaDdqUQPsGQw+J0MgaYIjQOCVASHAPlzbDQLCtuOb587rwTLkZA2GwoHB/LyJ
+BwT0HHgBaiObE12Vs6wi2en0Uu11CiwEuK1KIBcZ2XbE6eApaZa6VH9ysEmUxPt7
+TqyZ4E2oMIYaLPNRxuvozdwTlj1svI1k1FrkaXGc5MTjbgigPMKjIb0T7b/4GNzt
+DhP1LvAeUMnrEi3hJJrcJPXHPqS8/RiytR9xQQW6Sdh4LaA3f9MQm3WSevWage3G
+P8YcCLssOVKsArDjuA52NF5LmYuAeUzXprm4ITDi2oO+0iFBpFW6VPEK4A9vO0Yk
+M/6Wt6tG8zyWhaSH1zFUTwfQ9Yvjyt5w1lrUaAJuoTpwbMVZaDJaEhjOaXU0dyPQ
+jOsePDOQcU6dkeTWsQ3LsHPEEug/X6819TLG5mb3V7bvV9nPFBfTJSCEG794kr90
+XgZfIN71FrdByxLerlbuJI21pPs/nZi9SXi9jAWeiS45/azUxMsyYgJArui+gjq7
+sV1pWiBm6/orAgMBAAECggINQp5L6Yu+oIXBqcSjgq8tfF9M5hd30pLuf/EheHZf
+LA7uAqn2fVGFI2OInIJhXIOT5OxsAXO0xXfltzawZxIFpOFMqajj4F7aYjvSpw9V
+J4EdSiJ/zgv8y1qUdbwEZbHVThRZjoSlrtSzilonBoHZAE0mHtqMz7iRFSk1zz6t
+GunRrvo/lROPentf3TsvHquVNUYI5yaapyO1S7xJhecMIIYSb8nbsHI54FBDGNas
+6mFmpPwI/47/6HTwOEWupnn3NicsjrHzUInOUpaMig4cRR+aP5bjqg/ty8xI8AoN
+evEmCytiWTc+Rvbp1ieN+1jpjN18PjUk80/W7qioHUDt4ieLic8uxWH2VD9SCEnX
+Mpi9tA/FqoZ+2A/3m1OfrY6jiZVE2g+asi9lCK7QVWL39eK82H4rPvtp0/dyo1/i
+ZZz68TXg+m8IgEZcp88hngbkuoTTzpGE73QuPKhGA1uMIimDdqPPB5WP76q+03Oi
+IRR5DfZnqPERed49by0enJ7tKa/gFPZizOV8ALKr0Dp+vfAkxGDLPLBLd2A3//tw
+xg0Q/wltihHSBujv4nYlDXdc5oYyMYZ+Lhc/VuOghHfBq3tgEQ1ECM/ofqXEIdy7
+nVcpZn3Eeq8Jl5CrqxE1ee3NxlzsJHn99yGQpr7mOhW/psJF3XNz80Meg3L4m1T8
+sMBK0GbaassuJhdzb5whAoIBBw48sx1b1WR4XxQc5O/HjHva+l16i2pjUnOUTcDF
+RWmSbIhBm2QQ2rVhO8+fak0tkl6ZnMWW4i0U/X5LOEBbC7+IS8bO3j3Revi+Vw5x
+j96LMlIe9XEub5i/saEWgiz7maCvfzLFU08e1OpT4qPDpP293V400ubA6R7WQTCv
+pBkskGwHeu0l/TuKkVqBFFUTu7KEbps8Gjg7MkJaFriAOv1zis/umK8pVS3ZAM6e
+8w5jfpRccn8Xzta2fRwTB5kCmfxdDsY0oYGxPLRAbW72bORoLGuyyPp/ojeGwoik
+JX9RttErc6FjyZtks370Pa8UL5QskyhMbDhrZW2jFD+RXYM1BrvmZRjbAoIBBwy4
+iFJpuDfytJfz1MWtaL5DqEL/kmiZYAXl6hifNhGu5GAipVIIGsDqEYW4i+VC15aa
+7kOCwz/I5zsB3vSDW96IRs4wXtqEZSibc2W/bqfVi+xcvPPl1ZhQ2EAwa4D/x035
+kyf20ffWOU+1yf2cnijzqs3IzlveUm+meLw5s3Rc+iG7DPWWeCoe1hVwANI1euNc
+pqKwKY905yFyjOje2OgiEU2kS4YME4zGeBys8yo7E42hNnN2EPK6xkkUqzdudLLQ
+8OUlKRTc8AbIf3XG1rpA4VUpTv3hhxGGwCRy6If8zgZQsNYchgNztRGk72Gcb8Dm
+vFSEN3ZtwxU64G3YZzntdcr2WPzxAoIBBw30g6Fgdb/gmVnOpL0//T0ePNDKIMPs
+jVJLaRduhoZgB1Bb9qPUPX0SzRzLZtg1tkZSDjBDoHmOHJfhxUaXt+FLCPPbrE4t
++nq9n/nBaMM779w9ClqhqLOyGrwKoxjSmhi+TVEHyIxCbXMvPHVHfX9WzxjbcGrN
+ZvRaEVZWo+QlIX8yqdSwqxLk1WtAIRzvlcj7NKum8xBxPed6BNFep/PtgIAmoLT5
+L8wb7EWb2iUdc2KbZ4OaY51lDScqpATgXu3WjXfM+Q52G0mX6Wyd0cjlL711Zrjb
+yLbiueZT94lgIHHRRKtKc8CEqcjkQV5OzABS3P/gQSfgZXBdLKjOpTnKDUq7IBeH
+AoIBBweAOEIAPLQg1QRUrr3xRrYKRwlakgZDii9wJt1l5AgBTICzbTA1vzDJ1JM5
+AqSpCV6w9JWyYVcXK+HLdKBRZLaPPNEQDJ5lOxD6uMziWGl2rg8tj+1xNMWfxiPz
+aTCjoe4EoBUMoTq2gwzRcM2usEQNikXVhnj9Wzaivsaeb4bJ3GRPW5DkrO6JSEtT
+w+gvyMqQM2Hy5k7E7BT46sXVwaj/jZxuqGnebRixXtnp0WixdRIqYWUr1UqLf6hQ
+G7WP2BgoxCMaCmNW8+HMD/xuxucEotoIhZ+GgJKBFoNnjl3BX+qxYdSe9RbL/5Tr
+4It6Jxtj8uETJXEbv9Cg6v1agWPS9YY8RLTBAoIBBwrU2AsAUts6h1LgGLKK3UWZ
+oLH5E+4o+7HqSGRcRodVeN9NBXIYdHHOLeEG6YNGJiJ3bFP5ZQEu9iDsyoFVKJ9O
+Mw/y6dKZuxOCZ+X8FopSROg3yWfdOpAm6cnQZp3WqLNX4n/Q6WvKojfyEiPphjwT
+0ymrUJELXLWJmjUyPoAk6HgC0Gs28ZnEXbyhx7CSbZNFyCU/PNUDZwto3GisIPD3
+le7YjqHugezmjMGlA0sDw5aCXjfbl74vowRFYMO6e3ItApfSRgNV86CDoX74WI/5
+AYU/QVM4wGt8XGT2KwDFJaxYGKsGDMWmXY04dS+WPuetCbouWUusyFwRb9SzFave
+vYeU7Ab/
+-----END RSA PRIVATE KEY-----"""
+
+ec_private_key_pem = b"""-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgYirTZSx+5O8Y6tlG
+cka6W6btJiocdrdolfcukSoTEk+hRANCAAQkvPNu7Pa1GcsWU4v7ptNfqCJVq8Cx
+zo0MUVPQgwJ3aJtNM1QMOQUayCrRwfklg+D/rFSUwEUqtZh7fJDiFqz3
+-----END PRIVATE KEY-----
+"""
+
+ec_root_key_pem = b"""-----BEGIN EC PRIVATE KEY-----
+MIGlAgEBBDEAz/HOBFPYLB0jLWeTpJn4Yc4m/C4mdWymVHBjOmnwiPHKT326iYN/
+ZhmSs+RM94RsoAcGBSuBBAAioWQDYgAEwE5vDdla/nLpWAPAQ0yFGqwLuw4BcN2r
+U+sKab5EAEHzLeceRa8ffncYdCXNoVsBcdob1y66CFZMEWLetPTmGapyWkBAs6/L
+8kUlkU9OsE+7IVo4QQJkgV5gM+Dim1XE
+-----END EC PRIVATE KEY-----
+"""
+
+ec_root_cert_pem = b"""-----BEGIN CERTIFICATE-----
+MIICLTCCAbKgAwIBAgIMWW/hwTl6ufz6/WkCMAoGCCqGSM49BAMDMFgxGDAWBgNV
+BAMTD1Rlc3RpbmcgUm9vdCBDQTEQMA4GA1UEChMHVGVzdGluZzEQMA4GA1UEBxMH
+Q2hpY2FnbzELMAkGA1UECBMCSUwxCzAJBgNVBAYTAlVTMCAXDTE3MDcxOTIyNDgz
+M1oYDzk5OTkxMjMxMjM1OTU5WjBYMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0Ex
+EDAOBgNVBAoTB1Rlc3RpbmcxEDAOBgNVBAcTB0NoaWNhZ28xCzAJBgNVBAgTAklM
+MQswCQYDVQQGEwJVUzB2MBAGByqGSM49AgEGBSuBBAAiA2IABMBObw3ZWv5y6VgD
+wENMhRqsC7sOAXDdq1PrCmm+RABB8y3nHkWvH353GHQlzaFbAXHaG9cuughWTBFi
+3rT05hmqclpAQLOvy/JFJZFPTrBPuyFaOEECZIFeYDPg4ptVxKNDMEEwDwYDVR0T
+AQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwQAMB0GA1UdDgQWBBSoTrF0H2m8RDzB
+MnY2KReEPfz7ZjAKBggqhkjOPQQDAwNpADBmAjEA3+G1oVCxGjYX4iUN93QYcNHe
+e3fJQJwX9+KsHRut6qNZDUbvRbtO1YIAwB4UJZjwAjEAtXCPURS5A4McZHnSwgTi
+Td8GMrwKz0557OxxtKN6uVVy4ACFMqEw0zN/KJI1vxc9
+-----END CERTIFICATE-----"""
+
+
+@pytest.fixture
+def x509_data():
+ """
+ Create a new private key and start a certificate request (for a test
+ to finish in one way or another).
+ """
+ # Basic setup stuff to generate a certificate
+ pkey = PKey()
+ pkey.generate_key(TYPE_RSA, 512)
+ req = X509Req()
+ req.set_pubkey(pkey)
+ # Authority good you have.
+ req.get_subject().commonName = "Yoda root CA"
+ x509 = X509()
+ subject = x509.get_subject()
+ subject.commonName = req.get_subject().commonName
+ x509.set_issuer(subject)
+ x509.set_pubkey(pkey)
+ now = datetime.now()
+ expire = datetime.now() + timedelta(days=100)
+ x509.set_notBefore(now.strftime("%Y%m%d%H%M%SZ").encode())
+ x509.set_notAfter(expire.strftime("%Y%m%d%H%M%SZ").encode())
+ yield pkey, x509
+
+
+class TestX509Ext(object):
+ """
+ Tests for `OpenSSL.crypto.X509Extension`.
+ """
+
+ def test_str(self):
+ """
+ The string representation of `X509Extension` instances as
+ returned by `str` includes stuff.
+ """
+ # This isn't necessarily the best string representation. Perhaps it
+ # will be changed/improved in the future.
+ assert (
+ str(X509Extension(b'basicConstraints', True, b'CA:false')) ==
+ 'CA:FALSE'
+ )
+
+ def test_type(self):
+ """
+ `X509Extension` and `X509ExtensionType` refer to the same type object
+ and can be used to create instances of that type.
+ """
+ assert X509Extension is X509ExtensionType
+ assert is_consistent_type(
+ X509Extension,
+ 'X509Extension', b'basicConstraints', True, b'CA:true')
+
+ def test_construction(self):
+ """
+ `X509Extension` accepts an extension type name, a critical flag,
+ and an extension value and returns an `X509ExtensionType` instance.
+ """
+ basic = X509Extension(b'basicConstraints', True, b'CA:true')
+ assert isinstance(basic, X509ExtensionType)
+
+ comment = X509Extension(b'nsComment', False, b'pyOpenSSL unit test')
+ assert isinstance(comment, X509ExtensionType)
+
+ @pytest.mark.parametrize('type_name, critical, value', [
+ (b'thisIsMadeUp', False, b'hi'),
+ (b'basicConstraints', False, b'blah blah'),
+
+ # Exercise a weird one (an extension which uses the r2i method). This
+ # exercises the codepath that requires a non-NULL ctx to be passed to
+ # X509V3_EXT_nconf. It can't work now because we provide no
+ # configuration database. It might be made to work in the future.
+ (b'proxyCertInfo', True,
+ b'language:id-ppl-anyLanguage,pathlen:1,policy:text:AB')
+ ])
+ def test_invalid_extension(self, type_name, critical, value):
+ """
+ `X509Extension` raises something if it is passed a bad
+ extension name or value.
+ """
+ with pytest.raises(Error):
+ X509Extension(type_name, critical, value)
+
+ @pytest.mark.parametrize('critical_flag', [True, False])
+ def test_get_critical(self, critical_flag):
+ """
+ `X509ExtensionType.get_critical` returns the value of the
+ extension's critical flag.
+ """
+ ext = X509Extension(b'basicConstraints', critical_flag, b'CA:true')
+ assert ext.get_critical() == critical_flag
+
+ @pytest.mark.parametrize('short_name, value', [
+ (b'basicConstraints', b'CA:true'),
+ (b'nsComment', b'foo bar'),
+ ])
+ def test_get_short_name(self, short_name, value):
+ """
+ `X509ExtensionType.get_short_name` returns a string giving the
+ short type name of the extension.
+ """
+ ext = X509Extension(short_name, True, value)
+ assert ext.get_short_name() == short_name
+
+ def test_get_data(self):
+ """
+ `X509Extension.get_data` returns a string giving the data of
+ the extension.
+ """
+ ext = X509Extension(b'basicConstraints', True, b'CA:true')
+ # Expect to get back the DER encoded form of CA:true.
+ assert ext.get_data() == b'0\x03\x01\x01\xff'
+
+ def test_unused_subject(self, x509_data):
+ """
+ The `subject` parameter to `X509Extension` may be provided for an
+ extension which does not use it and is ignored in this case.
+ """
+ pkey, x509 = x509_data
+ ext1 = X509Extension(
+ b'basicConstraints', False, b'CA:TRUE', subject=x509)
+ x509.add_extensions([ext1])
+ x509.sign(pkey, 'sha1')
+ # This is a little lame. Can we think of a better way?
+ text = dump_certificate(FILETYPE_TEXT, x509)
+ assert b'X509v3 Basic Constraints:' in text
+ assert b'CA:TRUE' in text
+
+ def test_subject(self, x509_data):
+ """
+ If an extension requires a subject, the `subject` parameter to
+ `X509Extension` provides its value.
+ """
+ pkey, x509 = x509_data
+ ext3 = X509Extension(
+ b'subjectKeyIdentifier', False, b'hash', subject=x509)
+ x509.add_extensions([ext3])
+ x509.sign(pkey, 'sha1')
+ text = dump_certificate(FILETYPE_TEXT, x509)
+ assert b'X509v3 Subject Key Identifier:' in text
+
+ def test_missing_subject(self):
+ """
+ If an extension requires a subject and the `subject` parameter
+ is given no value, something happens.
+ """
+ with pytest.raises(Error):
+ X509Extension(b'subjectKeyIdentifier', False, b'hash')
+
+ @pytest.mark.parametrize('bad_obj', [
+ True,
+ object(),
+ "hello",
+ [],
+ ])
+ def test_invalid_subject(self, bad_obj):
+ """
+ If the `subject` parameter is given a value which is not an
+ `X509` instance, `TypeError` is raised.
+ """
+ with pytest.raises(TypeError):
+ X509Extension(
+ 'basicConstraints', False, 'CA:TRUE', subject=bad_obj)
+
+ def test_unused_issuer(self, x509_data):
+ """
+ The `issuer` parameter to `X509Extension` may be provided for an
+ extension which does not use it and is ignored in this case.
+ """
+ pkey, x509 = x509_data
+ ext1 = X509Extension(
+ b'basicConstraints', False, b'CA:TRUE', issuer=x509)
+ x509.add_extensions([ext1])
+ x509.sign(pkey, 'sha1')
+ text = dump_certificate(FILETYPE_TEXT, x509)
+ assert b'X509v3 Basic Constraints:' in text
+ assert b'CA:TRUE' in text
+
+ def test_issuer(self, x509_data):
+ """
+ If an extension requires an issuer, the `issuer` parameter to
+ `X509Extension` provides its value.
+ """
+ pkey, x509 = x509_data
+ ext2 = X509Extension(
+ b'authorityKeyIdentifier', False, b'issuer:always',
+ issuer=x509)
+ x509.add_extensions([ext2])
+ x509.sign(pkey, 'sha1')
+ text = dump_certificate(FILETYPE_TEXT, x509)
+ assert b'X509v3 Authority Key Identifier:' in text
+ assert b'DirName:/CN=Yoda root CA' in text
+
+ def test_missing_issuer(self):
+ """
+ If an extension requires an issue and the `issuer` parameter is
+ given no value, something happens.
+ """
+ with pytest.raises(Error):
+ X509Extension(
+ b'authorityKeyIdentifier',
+ False, b'keyid:always,issuer:always')
+
+ @pytest.mark.parametrize('bad_obj', [
+ True,
+ object(),
+ "hello",
+ [],
+ ])
+ def test_invalid_issuer(self, bad_obj):
+ """
+ If the `issuer` parameter is given a value which is not an
+ `X509` instance, `TypeError` is raised.
+ """
+ with pytest.raises(TypeError):
+ X509Extension(
+ 'basicConstraints', False, 'keyid:always,issuer:always',
+ issuer=bad_obj)
+
+
+class TestPKey(object):
+ """
+ Tests for `OpenSSL.crypto.PKey`.
+ """
+
+ def test_convert_from_cryptography_private_key(self):
+ """
+ PKey.from_cryptography_key creates a proper private PKey.
+ """
+ key = serialization.load_pem_private_key(
+ intermediate_key_pem, None, backend
+ )
+ pkey = PKey.from_cryptography_key(key)
+
+ assert isinstance(pkey, PKey)
+ assert pkey.bits() == key.key_size
+ assert pkey._only_public is False
+ assert pkey._initialized is True
+
+ def test_convert_from_cryptography_public_key(self):
+ """
+ PKey.from_cryptography_key creates a proper public PKey.
+ """
+ key = serialization.load_pem_public_key(cleartextPublicKeyPEM, backend)
+ pkey = PKey.from_cryptography_key(key)
+
+ assert isinstance(pkey, PKey)
+ assert pkey.bits() == key.key_size
+ assert pkey._only_public is True
+ assert pkey._initialized is True
+
+ def test_convert_from_cryptography_unsupported_type(self):
+ """
+ PKey.from_cryptography_key raises TypeError with an unsupported type.
+ """
+ key = serialization.load_pem_private_key(
+ ec_private_key_pem, None, backend
+ )
+ with pytest.raises(TypeError):
+ PKey.from_cryptography_key(key)
+
+ def test_convert_public_pkey_to_cryptography_key(self):
+ """
+ PKey.to_cryptography_key creates a proper cryptography public key.
+ """
+ pkey = load_publickey(FILETYPE_PEM, cleartextPublicKeyPEM)
+ key = pkey.to_cryptography_key()
+
+ assert isinstance(key, rsa.RSAPublicKey)
+ assert pkey.bits() == key.key_size
+
+ def test_convert_private_pkey_to_cryptography_key(self):
+ """
+ PKey.to_cryptography_key creates a proper cryptography private key.
+ """
+ pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ key = pkey.to_cryptography_key()
+
+ assert isinstance(key, rsa.RSAPrivateKey)
+ assert pkey.bits() == key.key_size
+
+ def test_type(self):
+ """
+ `PKey` and `PKeyType` refer to the same type object and can be used to
+ create instances of that type.
+ """
+ assert PKey is PKeyType
+ assert is_consistent_type(PKey, 'PKey')
+
+ def test_construction(self):
+ """
+ `PKey` takes no arguments and returns a new `PKey` instance.
+ """
+ key = PKey()
+ assert isinstance(key, PKey)
+
+ def test_pregeneration(self):
+ """
+ `PKey.bits` and `PKey.type` return `0` before the key is generated.
+ `PKey.check` raises `TypeError` before the key is generated.
+ """
+ key = PKey()
+ assert key.type() == 0
+ assert key.bits() == 0
+ with pytest.raises(TypeError):
+ key.check()
+
+ def test_failed_generation(self):
+ """
+ `PKey.generate_key` takes two arguments, the first giving the key type
+ as one of `TYPE_RSA` or `TYPE_DSA` and the second giving the number of
+ bits to generate. If an invalid type is specified or generation fails,
+ `Error` is raised. If an invalid number of bits is specified,
+ `ValueError` or `Error` is raised.
+ """
+ key = PKey()
+ with pytest.raises(TypeError):
+ key.generate_key("foo", "bar")
+ with pytest.raises(Error):
+ key.generate_key(-1, 0)
+
+ with pytest.raises(ValueError):
+ key.generate_key(TYPE_RSA, -1)
+ with pytest.raises(ValueError):
+ key.generate_key(TYPE_RSA, 0)
+
+ with pytest.raises(TypeError):
+ key.generate_key(TYPE_RSA, object())
+
+ # XXX RSA generation for small values of bits is fairly buggy in a wide
+ # range of OpenSSL versions. I need to figure out what the safe lower
+ # bound for a reasonable number of OpenSSL versions is and explicitly
+ # check for that in the wrapper. The failure behavior is typically an
+ # infinite loop inside OpenSSL.
+
+ # with pytest.raises(Error):
+ # key.generate_key(TYPE_RSA, 2)
+
+ # XXX DSA generation seems happy with any number of bits. The DSS
+ # says bits must be between 512 and 1024 inclusive. OpenSSL's DSA
+ # generator doesn't seem to care about the upper limit at all. For
+ # the lower limit, it uses 512 if anything smaller is specified.
+ # So, it doesn't seem possible to make generate_key fail for
+ # TYPE_DSA with a bits argument which is at least an int.
+
+ # with pytest.raises(Error):
+ # key.generate_key(TYPE_DSA, -7)
+
+ def test_rsa_generation(self):
+ """
+ `PKey.generate_key` generates an RSA key when passed `TYPE_RSA` as a
+ type and a reasonable number of bits.
+ """
+ bits = 512
+ key = PKey()
+ key.generate_key(TYPE_RSA, bits)
+ assert key.type() == TYPE_RSA
+ assert key.bits() == bits
+ assert key.check()
+
+ def test_dsa_generation(self):
+ """
+ `PKey.generate_key` generates a DSA key when passed `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
+ # will silently promote any value below 512 to 512.
+ bits = 512
+ key = PKey()
+ key.generate_key(TYPE_DSA, bits)
+ assert key.type() == TYPE_DSA
+ assert key.bits() == bits
+ with pytest.raises(TypeError):
+ key.check()
+
+ def test_regeneration(self):
+ """
+ `PKey.generate_key` can be called multiple times on the same key to
+ generate new keys.
+ """
+ key = PKey()
+ for type, bits in [(TYPE_RSA, 512), (TYPE_DSA, 576)]:
+ key.generate_key(type, bits)
+ assert key.type() == type
+ assert key.bits() == bits
+
+ def test_inconsistent_key(self):
+ """
+ `PKey.check` returns `Error` if the key is not consistent.
+ """
+ key = load_privatekey(FILETYPE_PEM, inconsistentPrivateKeyPEM)
+ with pytest.raises(Error):
+ key.check()
+
+ def test_check_public_key(self):
+ """
+ `PKey.check` raises `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()
+ with pytest.raises(TypeError):
+ pub.check()
+
+
+def x509_name(**attrs):
+ """
+ Return a new X509Name with the given attributes.
+ """
+ # XXX There's no other way to get a new X509Name yet.
+ name = X509().get_subject()
+ attrs = list(attrs.items())
+
+ # Make the order stable - order matters!
+ def key(attr):
+ return attr[1]
+ attrs.sort(key=key)
+ for k, v in attrs:
+ setattr(name, k, v)
+ return name
+
+
+class TestX509Name(object):
+ """
+ Unit tests for `OpenSSL.crypto.X509Name`.
+ """
+
+ def test_type(self):
+ """
+ The type of X509Name objects is `X509NameType`.
+ """
+ assert X509Name is X509NameType
+ assert X509NameType.__name__ == 'X509Name'
+ assert isinstance(X509NameType, type)
+
+ name = x509_name()
+ assert isinstance(name, X509NameType)
+
+ def test_only_string_attributes(self):
+ """
+ Attempting to set a non-`str` attribute name on an `X509Name` instance
+ causes `TypeError` to be raised.
+ """
+ name = x509_name()
+ # 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.
+ with pytest.raises(TypeError):
+ setattr(name, None, "hello")
+ with pytest.raises(TypeError):
+ setattr(name, 30, "hello")
+
+ def test_set_invalid_attribute(self):
+ """
+ Attempting to set any attribute name on an `X509Name` instance for
+ which no corresponding NID is defined causes `AttributeError` to be
+ raised.
+ """
+ name = x509_name()
+ with pytest.raises(AttributeError):
+ setattr(name, "no such thing", None)
+
+ def test_attributes(self):
+ """
+ `X509Name` instances have attributes for each standard (?)
+ X509Name field.
+ """
+ name = x509_name()
+ name.commonName = "foo"
+ assert name.commonName == "foo"
+ assert name.CN == "foo"
+
+ name.CN = "baz"
+ assert name.commonName == "baz"
+ assert name.CN == "baz"
+
+ name.commonName = "bar"
+ assert name.commonName == "bar"
+ assert name.CN == "bar"
+
+ name.CN = "quux"
+ assert name.commonName == "quux"
+ assert name.CN == "quux"
+
+ assert name.OU is None
+
+ with pytest.raises(AttributeError):
+ name.foobar
+
+ def test_copy(self):
+ """
+ `X509Name` creates a new `X509Name` instance with all the same
+ attributes as an existing `X509Name` instance when called with one.
+ """
+ name = x509_name(commonName="foo", emailAddress="bar@example.com")
+
+ copy = X509Name(name)
+ assert copy.commonName == "foo"
+ assert copy.emailAddress == "bar@example.com"
+
+ # Mutate the copy and ensure the original is unmodified.
+ copy.commonName = "baz"
+ assert name.commonName == "foo"
+
+ # Mutate the original and ensure the copy is unmodified.
+ name.emailAddress = "quux@example.com"
+ assert copy.emailAddress == "bar@example.com"
+
+ def test_repr(self):
+ """
+ `repr` passed an `X509Name` instance should return a string containing
+ a description of the type and the NIDs which have been set on it.
+ """
+ name = x509_name(commonName="foo", emailAddress="bar")
+ assert repr(name) == "<X509Name object '/emailAddress=bar/CN=foo'>"
+
+ def test_comparison(self):
+ """
+ `X509Name` instances should compare based on their NIDs.
+ """
+ def _equality(a, b, assert_true, assert_false):
+ assert_true(a == b)
+ assert_false(a != b)
+ assert_true(b == a)
+ assert_false(b != a)
+
+ def assert_true(x):
+ assert x
+
+ def assert_false(x):
+ assert not x
+
+ def assert_equal(a, b):
+ _equality(a, b, assert_true, assert_false)
+
+ # Instances compare equal to themselves.
+ name = x509_name()
+ assert_equal(name, name)
+
+ # Empty instances should compare equal to each other.
+ assert_equal(x509_name(), x509_name())
+
+ # Instances with equal NIDs should compare equal to each other.
+ assert_equal(x509_name(commonName="foo"),
+ x509_name(commonName="foo"))
+
+ # Instance with equal NIDs set using different aliases should compare
+ # equal to each other.
+ assert_equal(x509_name(commonName="foo"),
+ x509_name(CN="foo"))
+
+ # Instances with more than one NID with the same values should compare
+ # equal to each other.
+ assert_equal(x509_name(CN="foo", organizationalUnitName="bar"),
+ x509_name(commonName="foo", OU="bar"))
+
+ def assert_not_equal(a, b):
+ _equality(a, b, assert_false, assert_true)
+
+ # Instances with different values for the same NID should not compare
+ # equal to each other.
+ assert_not_equal(x509_name(CN="foo"),
+ x509_name(CN="bar"))
+
+ # Instances with different NIDs should not compare equal to each other.
+ assert_not_equal(x509_name(CN="foo"),
+ x509_name(OU="foo"))
+
+ assert_not_equal(x509_name(), object())
+
+ def _inequality(a, b, assert_true, assert_false):
+ assert_true(a < b)
+ assert_true(a <= b)
+ assert_true(b > a)
+ assert_true(b >= a)
+ assert_false(a > b)
+ assert_false(a >= b)
+ assert_false(b < a)
+ assert_false(b <= a)
+
+ def assert_less_than(a, b):
+ _inequality(a, b, assert_true, assert_false)
+
+ # An X509Name with a NID with a value which sorts less than the value
+ # of the same NID on another X509Name compares less than the other
+ # X509Name.
+ assert_less_than(x509_name(CN="abc"),
+ x509_name(CN="def"))
+
+ def assert_greater_than(a, b):
+ _inequality(a, b, assert_false, assert_true)
+
+ # An X509Name with a NID with a value which sorts greater than the
+ # value of the same NID on another X509Name compares greater than the
+ # other X509Name.
+ assert_greater_than(x509_name(CN="def"),
+ x509_name(CN="abc"))
+
+ def test_hash(self):
+ """
+ `X509Name.hash` returns an integer hash based on the value of the name.
+ """
+ a = x509_name(CN="foo")
+ b = x509_name(CN="foo")
+ assert a.hash() == b.hash()
+ a.CN = "bar"
+ assert a.hash() != b.hash()
+
+ def test_der(self):
+ """
+ `X509Name.der` returns the DER encoded form of the name.
+ """
+ a = x509_name(CN="foo", C="US")
+ assert (a.der() ==
+ b'0\x1b1\x0b0\t\x06\x03U\x04\x06\x13\x02US'
+ b'1\x0c0\n\x06\x03U\x04\x03\x0c\x03foo')
+
+ def test_get_components(self):
+ """
+ `X509Name.get_components` returns a `list` of two-tuples of `str`
+ giving the NIDs and associated values which make up the name.
+ """
+ a = x509_name()
+ assert a.get_components() == []
+ a.CN = "foo"
+ assert a.get_components() == [(b"CN", b"foo")]
+ a.organizationalUnitName = "bar"
+ assert a.get_components() == [(b"CN", b"foo"), (b"OU", b"bar")]
+
+ def test_load_nul_byte_attribute(self):
+ """
+ An `X509Name` from an `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()
+ assert "null.python.org\x00example.org" == subject.commonName
+
+ def test_load_nul_byte_components(self):
+ """
+ An `X509Name` from an `X509` instance loaded from a file can have a
+ NUL byte in the value of its components
+ """
+ cert = load_certificate(FILETYPE_PEM, nulbyteSubjectAltNamePEM)
+ subject = cert.get_subject()
+ components = subject.get_components()
+ ccn = [value for name, value in components if name == b'CN']
+ assert ccn[0] == b'null.python.org\x00example.org'
+
+ def test_set_attribute_failure(self):
+ """
+ If the value of an attribute cannot be set for some reason then
+ `Error` is raised.
+ """
+ name = x509_name()
+ # This value is too long
+ with pytest.raises(Error):
+ setattr(name, "O", b"x" * 512)
+
+
+class _PKeyInteractionTestsMixin:
+ """
+ Tests which involve another thing and a PKey.
+ """
+
+ def signable(self):
+ """
+ Return something with a `set_pubkey`, `set_pubkey`, and `sign` method.
+ """
+ raise NotImplementedError()
+
+ def test_sign_with_ungenerated(self):
+ """
+ `X509Req.sign` raises `ValueError` when passed a `PKey` with no parts.
+ """
+ request = self.signable()
+ key = PKey()
+ with pytest.raises(ValueError):
+ request.sign(key, GOOD_DIGEST)
+
+ def test_sign_with_public_key(self):
+ """
+ `X509Req.sign` raises `ValueError` when passed a `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()
+ with pytest.raises(ValueError):
+ request.sign(pub, GOOD_DIGEST)
+
+ def test_sign_with_unknown_digest(self):
+ """
+ `X509Req.sign` raises `ValueError` when passed a digest name which is
+ not known.
+ """
+ request = self.signable()
+ key = PKey()
+ key.generate_key(TYPE_RSA, 512)
+ with pytest.raises(ValueError):
+ request.sign(key, BAD_DIGEST)
+
+ def test_sign(self):
+ """
+ `X509Req.sign` succeeds when passed a private key object and a
+ valid digest function. `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, GOOD_DIGEST)
+ # If the type has a verify method, cover that too.
+ if getattr(request, 'verify', None) is not None:
+ pub = request.get_pubkey()
+ assert request.verify(pub)
+ # Make another key that won't verify.
+ key = PKey()
+ key.generate_key(TYPE_RSA, 512)
+ with pytest.raises(Error):
+ request.verify(key)
+
+
+class TestX509Req(_PKeyInteractionTestsMixin):
+ """
+ Tests for `OpenSSL.crypto.X509Req`.
+ """
+
+ def signable(self):
+ """
+ Create and return a new `X509Req`.
+ """
+ return X509Req()
+
+ def test_type(self):
+ """
+ `X509Req` and `X509ReqType` refer to the same type object and can be
+ used to create instances of that type.
+ """
+ assert X509Req is X509ReqType
+ assert is_consistent_type(X509Req, 'X509Req')
+
+ def test_construction(self):
+ """
+ `X509Req` takes no arguments and returns an `X509ReqType` instance.
+ """
+ request = X509Req()
+ assert isinstance(request, X509ReqType)
+
+ def test_version(self):
+ """
+ `X509Req.set_version` sets the X.509 version of the certificate
+ request. `X509Req.get_version` returns the X.509 version of the
+ certificate request. The initial value of the version is 0.
+ """
+ request = X509Req()
+ assert request.get_version() == 0
+ request.set_version(1)
+ assert request.get_version() == 1
+ request.set_version(3)
+ assert request.get_version() == 3
+
+ def test_version_wrong_args(self):
+ """
+ `X509Req.set_version` raises `TypeError` if called with a non-`int`
+ argument.
+ """
+ request = X509Req()
+ with pytest.raises(TypeError):
+ request.set_version("foo")
+
+ def test_get_subject(self):
+ """
+ `X509Req.get_subject` returns an `X509Name` for the subject of the
+ request and which is valid even after the request object is
+ otherwise dead.
+ """
+ request = X509Req()
+ subject = request.get_subject()
+ assert isinstance(subject, X509NameType)
+ subject.commonName = "foo"
+ assert request.get_subject().commonName == "foo"
+ del request
+ subject.commonName = "bar"
+ assert subject.commonName == "bar"
+
+ def test_add_extensions(self):
+ """
+ `X509Req.add_extensions` accepts a `list` of `X509Extension` instances
+ and adds them to the X509 request.
+ """
+ request = X509Req()
+ request.add_extensions([
+ X509Extension(b'basicConstraints', True, b'CA:false')])
+ exts = request.get_extensions()
+ assert len(exts) == 1
+ assert exts[0].get_short_name() == b'basicConstraints'
+ assert exts[0].get_critical() == 1
+ assert exts[0].get_data() == b'0\x00'
+
+ def test_get_extensions(self):
+ """
+ `X509Req.get_extensions` returns a `list` of extensions added to this
+ X509 request.
+ """
+ request = X509Req()
+ exts = request.get_extensions()
+ assert exts == []
+ request.add_extensions([
+ X509Extension(b'basicConstraints', True, b'CA:true'),
+ X509Extension(b'keyUsage', False, b'digitalSignature')])
+ exts = request.get_extensions()
+ assert len(exts) == 2
+ assert exts[0].get_short_name() == b'basicConstraints'
+ assert exts[0].get_critical() == 1
+ assert exts[0].get_data() == b'0\x03\x01\x01\xff'
+ assert exts[1].get_short_name() == b'keyUsage'
+ assert exts[1].get_critical() == 0
+ assert exts[1].get_data() == b'\x03\x02\x07\x80'
+
+ def test_add_extensions_wrong_args(self):
+ """
+ `X509Req.add_extensions` raises `TypeError` if called with a
+ non-`list`. Or it raises `ValueError` if called with a `list`
+ containing objects other than `X509Extension` instances.
+ """
+ request = X509Req()
+ with pytest.raises(TypeError):
+ request.add_extensions(object())
+ with pytest.raises(ValueError):
+ request.add_extensions([object()])
+
+ def test_verify_wrong_args(self):
+ """
+ `X509Req.verify` raises `TypeError` if passed anything other than a
+ `PKey` instance as its single argument.
+ """
+ request = X509Req()
+ with pytest.raises(TypeError):
+ request.verify(object())
+
+ def test_verify_uninitialized_key(self):
+ """
+ `X509Req.verify` raises `OpenSSL.crypto.Error` if called with a
+ `OpenSSL.crypto.PKey` which contains no key data.
+ """
+ request = X509Req()
+ pkey = PKey()
+ with pytest.raises(Error):
+ request.verify(pkey)
+
+ def test_verify_wrong_key(self):
+ """
+ `X509Req.verify` raises `OpenSSL.crypto.Error` if called with a
+ `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)
+ with pytest.raises(Error):
+ request.verify(another_pkey)
+
+ def test_verify_success(self):
+ """
+ `X509Req.verify` returns `True` if called with a `OpenSSL.crypto.PKey`
+ which represents the public part of the key which signed the request.
+ """
+ request = X509Req()
+ pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ request.sign(pkey, GOOD_DIGEST)
+ assert request.verify(pkey)
+
+ def test_convert_from_cryptography(self):
+ crypto_req = x509.load_pem_x509_csr(
+ cleartextCertificateRequestPEM, backend
+ )
+ req = X509Req.from_cryptography(crypto_req)
+ assert isinstance(req, X509Req)
+
+ def test_convert_from_cryptography_unsupported_type(self):
+ with pytest.raises(TypeError):
+ X509Req.from_cryptography(object())
+
+ def test_convert_to_cryptography_key(self):
+ req = load_certificate_request(
+ FILETYPE_PEM, cleartextCertificateRequestPEM
+ )
+ crypto_req = req.to_cryptography()
+ assert isinstance(crypto_req, x509.CertificateSigningRequest)
+
+
+class TestX509(_PKeyInteractionTestsMixin):
+ """
+ Tests for `OpenSSL.crypto.X509`.
+ """
+ pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
+
+ extpem = """
+-----BEGIN CERTIFICATE-----
+MIIC3jCCAkegAwIBAgIJAJHFjlcCgnQzMA0GCSqGSIb3DQEBBQUAMEcxCzAJBgNV
+BAYTAlNFMRUwEwYDVQQIEwxXZXN0ZXJib3R0b20xEjAQBgNVBAoTCUNhdGFsb2dp
+eDENMAsGA1UEAxMEUm9vdDAeFw0wODA0MjIxNDQ1MzhaFw0wOTA0MjIxNDQ1Mzha
+MFQxCzAJBgNVBAYTAlNFMQswCQYDVQQIEwJXQjEUMBIGA1UEChMLT3Blbk1ldGFk
+aXIxIjAgBgNVBAMTGW5vZGUxLm9tMi5vcGVubWV0YWRpci5vcmcwgZ8wDQYJKoZI
+hvcNAQEBBQADgY0AMIGJAoGBAPIcQMrwbk2nESF/0JKibj9i1x95XYAOwP+LarwT
+Op4EQbdlI9SY+uqYqlERhF19w7CS+S6oyqx0DRZSk4Y9dZ9j9/xgm2u/f136YS1u
+zgYFPvfUs6PqYLPSM8Bw+SjJ+7+2+TN+Tkiof9WP1cMjodQwOmdsiRbR0/J7+b1B
+hec1AgMBAAGjgcQwgcEwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNT
+TCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFIdHsBcMVVMbAO7j6NCj
+03HgLnHaMB8GA1UdIwQYMBaAFL2h9Bf9Mre4vTdOiHTGAt7BRY/8MEYGA1UdEQQ/
+MD2CDSouZXhhbXBsZS5vcmeCESoub20yLmV4bWFwbGUuY29thwSC7wgKgRNvbTJA
+b3Blbm1ldGFkaXIub3JnMA0GCSqGSIb3DQEBBQUAA4GBALd7WdXkp2KvZ7/PuWZA
+MPlIxyjS+Ly11+BNE0xGQRp9Wz+2lABtpgNqssvU156+HkKd02rGheb2tj7MX9hG
+uZzbwDAZzJPjzDQDD7d3cWsrVcfIdqVU7epHqIadnOF+X0ghJ39pAm6VVadnSXCt
+WpOdIpB8KksUTCzV591Nr1wd
+-----END CERTIFICATE-----
+ """
+
+ def signable(self):
+ """
+ Create and return a new `X509`.
+ """
+ return X509()
+
+ def test_type(self):
+ """
+ `X509` and `X509Type` refer to the same type object and can be used to
+ create instances of that type.
+ """
+ assert X509 is X509Type
+ assert is_consistent_type(X509, 'X509')
+
+ def test_construction(self):
+ """
+ `X509` takes no arguments and returns an instance of `X509Type`.
+ """
+ certificate = X509()
+ assert isinstance(certificate, X509Type)
+ assert type(X509Type).__name__ == 'type'
+ assert type(certificate).__name__ == 'X509'
+ assert type(certificate) == X509Type
+ assert type(certificate) == X509
+
+ def test_set_version_wrong_args(self):
+ """
+ `X509.set_version` raises `TypeError` if invoked with an argument
+ not of type `int`.
+ """
+ cert = X509()
+ with pytest.raises(TypeError):
+ cert.set_version(None)
+
+ def test_version(self):
+ """
+ `X509.set_version` sets the certificate version number.
+ `X509.get_version` retrieves it.
+ """
+ cert = X509()
+ cert.set_version(1234)
+ assert cert.get_version() == 1234
+
+ def test_serial_number(self):
+ """
+ The serial number of an `X509` can be retrieved and
+ modified with `X509.get_serial_number` and
+ `X509.set_serial_number`.
+ """
+ certificate = X509()
+ with pytest.raises(TypeError):
+ certificate.set_serial_number("1")
+ assert certificate.get_serial_number() == 0
+ certificate.set_serial_number(1)
+ assert certificate.get_serial_number() == 1
+ certificate.set_serial_number(2 ** 32 + 1)
+ assert certificate.get_serial_number() == 2 ** 32 + 1
+ certificate.set_serial_number(2 ** 64 + 1)
+ assert certificate.get_serial_number() == 2 ** 64 + 1
+ certificate.set_serial_number(2 ** 128 + 1)
+ assert certificate.get_serial_number() == 2 ** 128 + 1
+
+ def _setBoundTest(self, which):
+ """
+ `X509.set_notBefore` takes a string in the format of an
+ ASN1 GENERALIZEDTIME and sets the beginning of the certificate's
+ validity period to it.
+ """
+ certificate = X509()
+ set = getattr(certificate, 'set_not' + which)
+ get = getattr(certificate, 'get_not' + which)
+
+ # Starts with no value.
+ assert get() is None
+
+ # GMT (Or is it UTC?) -exarkun
+ when = b"20040203040506Z"
+ set(when)
+ assert get() == when
+
+ # A plus two hours and thirty minutes offset
+ when = b"20040203040506+0530"
+ set(when)
+ assert get() == when
+
+ # A minus one hour fifteen minutes offset
+ when = b"20040203040506-0115"
+ set(when)
+ assert get() == when
+
+ # An invalid string results in a ValueError
+ with pytest.raises(ValueError):
+ set(b"foo bar")
+
+ # The wrong number of arguments results in a TypeError.
+ with pytest.raises(TypeError):
+ set()
+ with pytest.raises(TypeError):
+ set(b"20040203040506Z", b"20040203040506Z")
+ with pytest.raises(TypeError):
+ get(b"foo bar")
+
+ # XXX ASN1_TIME (not GENERALIZEDTIME)
+
+ def test_set_notBefore(self):
+ """
+ `X509.set_notBefore` takes a string in the format of an
+ ASN1 GENERALIZEDTIME and sets the beginning of the certificate's
+ validity period to it.
+ """
+ self._setBoundTest("Before")
+
+ def test_set_notAfter(self):
+ """
+ `X509.set_notAfter` takes a string in the format of an ASN1
+ GENERALIZEDTIME and sets the end of the certificate's validity period
+ to it.
+ """
+ self._setBoundTest("After")
+
+ def test_get_notBefore(self):
+ """
+ `X509.get_notBefore` returns a string in the format of an
+ ASN1 GENERALIZEDTIME even for certificates which store it as UTCTIME
+ internally.
+ """
+ cert = load_certificate(FILETYPE_PEM, old_root_cert_pem)
+ assert cert.get_notBefore() == b"20090325123658Z"
+
+ def test_get_notAfter(self):
+ """
+ `X509.get_notAfter` returns a string in the format of an
+ ASN1 GENERALIZEDTIME even for certificates which store it as UTCTIME
+ internally.
+ """
+ cert = load_certificate(FILETYPE_PEM, old_root_cert_pem)
+ assert cert.get_notAfter() == b"20170611123658Z"
+
+ def test_gmtime_adj_notBefore_wrong_args(self):
+ """
+ `X509.gmtime_adj_notBefore` raises `TypeError` if called with a
+ non-`int` argument.
+ """
+ cert = X509()
+ with pytest.raises(TypeError):
+ cert.gmtime_adj_notBefore(None)
+
+ @flaky.flaky
+ def test_gmtime_adj_notBefore(self):
+ """
+ `X509.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)
+ not_before_min = (
+ datetime.utcnow().replace(microsecond=0) + timedelta(seconds=100)
+ )
+ cert.gmtime_adj_notBefore(100)
+ not_before = datetime.strptime(
+ cert.get_notBefore().decode(), "%Y%m%d%H%M%SZ"
+ )
+ not_before_max = datetime.utcnow() + timedelta(seconds=100)
+ assert not_before_min <= not_before <= not_before_max
+
+ def test_gmtime_adj_notAfter_wrong_args(self):
+ """
+ `X509.gmtime_adj_notAfter` raises `TypeError` if called with a
+ non-`int` argument.
+ """
+ cert = X509()
+ with pytest.raises(TypeError):
+ cert.gmtime_adj_notAfter(None)
+
+ @flaky.flaky
+ def test_gmtime_adj_notAfter(self):
+ """
+ `X509.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)
+ not_after_min = (
+ datetime.utcnow().replace(microsecond=0) + timedelta(seconds=100)
+ )
+ cert.gmtime_adj_notAfter(100)
+ not_after = datetime.strptime(
+ cert.get_notAfter().decode(), "%Y%m%d%H%M%SZ"
+ )
+ not_after_max = datetime.utcnow() + timedelta(seconds=100)
+ assert not_after_min <= not_after <= not_after_max
+
+ def test_has_expired(self):
+ """
+ `X509.has_expired` returns `True` if the certificate's not-after time
+ is in the past.
+ """
+ cert = X509()
+ cert.gmtime_adj_notAfter(-1)
+ assert cert.has_expired()
+
+ def test_has_not_expired(self):
+ """
+ `X509.has_expired` returns `False` if the certificate's not-after time
+ is in the future.
+ """
+ cert = X509()
+ cert.gmtime_adj_notAfter(2)
+ assert not cert.has_expired()
+
+ def test_root_has_not_expired(self):
+ """
+ `X509.has_expired` returns `False` if the certificate's not-after time
+ is in the future.
+ """
+ cert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ assert not cert.has_expired()
+
+ def test_digest(self):
+ """
+ `X509.digest` returns a string giving ":"-separated hex-encoded
+ words of the digest of the certificate.
+ """
+ cert = load_certificate(FILETYPE_PEM, old_root_cert_pem)
+ assert (
+ # 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).
+ # Digest verified with the command:
+ # openssl x509 -in root_cert.pem -noout -fingerprint -md5
+ cert.digest("MD5") ==
+ b"19:B3:05:26:2B:F8:F2:FF:0B:8F:21:07:A8:28:B8:75")
+
+ def _extcert(self, pkey, extensions):
+ cert = X509()
+ cert.set_pubkey(pkey)
+ cert.get_subject().commonName = "Unit Tests"
+ cert.get_issuer().commonName = "Unit Tests"
+ when = datetime.now().strftime("%Y%m%d%H%M%SZ").encode("ascii")
+ cert.set_notBefore(when)
+ cert.set_notAfter(when)
+
+ cert.add_extensions(extensions)
+ cert.sign(pkey, 'sha1')
+ return load_certificate(
+ FILETYPE_PEM, dump_certificate(FILETYPE_PEM, cert))
+
+ def test_extension_count(self):
+ """
+ `X509.get_extension_count` returns the number of extensions
+ that are present in the certificate.
+ """
+ pkey = load_privatekey(FILETYPE_PEM, client_key_pem)
+ ca = X509Extension(b'basicConstraints', True, b'CA:FALSE')
+ key = X509Extension(b'keyUsage', True, b'digitalSignature')
+ subjectAltName = X509Extension(
+ b'subjectAltName', True, b'DNS:example.com')
+
+ # Try a certificate with no extensions at all.
+ c = self._extcert(pkey, [])
+ assert c.get_extension_count() == 0
+
+ # And a certificate with one
+ c = self._extcert(pkey, [ca])
+ assert c.get_extension_count() == 1
+
+ # And a certificate with several
+ c = self._extcert(pkey, [ca, key, subjectAltName])
+ assert c.get_extension_count() == 3
+
+ def test_get_extension(self):
+ """
+ `X509.get_extension` takes an integer and returns an
+ `X509Extension` corresponding to the extension at that index.
+ """
+ pkey = load_privatekey(FILETYPE_PEM, client_key_pem)
+ ca = X509Extension(b'basicConstraints', True, b'CA:FALSE')
+ key = X509Extension(b'keyUsage', True, b'digitalSignature')
+ subjectAltName = X509Extension(
+ b'subjectAltName', False, b'DNS:example.com')
+
+ cert = self._extcert(pkey, [ca, key, subjectAltName])
+
+ ext = cert.get_extension(0)
+ assert isinstance(ext, X509Extension)
+ assert ext.get_critical()
+ assert ext.get_short_name() == b'basicConstraints'
+
+ ext = cert.get_extension(1)
+ assert isinstance(ext, X509Extension)
+ assert ext.get_critical()
+ assert ext.get_short_name() == b'keyUsage'
+
+ ext = cert.get_extension(2)
+ assert isinstance(ext, X509Extension)
+ assert not ext.get_critical()
+ assert ext.get_short_name() == b'subjectAltName'
+
+ with pytest.raises(IndexError):
+ cert.get_extension(-1)
+ with pytest.raises(IndexError):
+ cert.get_extension(4)
+ with pytest.raises(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)
+ assert ext.get_short_name() == b'subjectAltName'
+ assert (
+ b"DNS:altnull.python.org\x00example.com, "
+ b"email:null@python.org\x00user@example.org, "
+ b"URI:http://null.python.org\x00http://example.org, "
+ b"IP Address:192.0.2.1, IP Address:2001:DB8:0:0:0:0:0:1\n" ==
+ str(ext).encode("ascii"))
+
+ def test_invalid_digest_algorithm(self):
+ """
+ `X509.digest` raises `ValueError` if called with an unrecognized hash
+ algorithm.
+ """
+ cert = X509()
+ with pytest.raises(ValueError):
+ cert.digest(BAD_DIGEST)
+
+ def test_get_subject(self):
+ """
+ `X509.get_subject` returns an `X509Name` instance.
+ """
+ cert = load_certificate(FILETYPE_PEM, self.pemData)
+ subj = cert.get_subject()
+ assert isinstance(subj, X509Name)
+ assert (
+ subj.get_components() ==
+ [(b'C', b'US'), (b'ST', b'IL'), (b'L', b'Chicago'),
+ (b'O', b'Testing'), (b'CN', b'Testing Root CA')])
+
+ def test_set_subject_wrong_args(self):
+ """
+ `X509.set_subject` raises a `TypeError` if called with an argument not
+ of type `X509Name`.
+ """
+ cert = X509()
+ with pytest.raises(TypeError):
+ cert.set_subject(None)
+
+ def test_set_subject(self):
+ """
+ `X509.set_subject` changes the subject of the certificate to the one
+ passed in.
+ """
+ cert = X509()
+ name = cert.get_subject()
+ name.C = 'AU'
+ name.OU = 'Unit Tests'
+ cert.set_subject(name)
+ assert (
+ cert.get_subject().get_components() ==
+ [(b'C', b'AU'), (b'OU', b'Unit Tests')])
+
+ def test_get_issuer(self):
+ """
+ `X509.get_issuer` returns an `X509Name` instance.
+ """
+ cert = load_certificate(FILETYPE_PEM, self.pemData)
+ subj = cert.get_issuer()
+ assert isinstance(subj, X509Name)
+ comp = subj.get_components()
+ assert (
+ comp ==
+ [(b'C', b'US'), (b'ST', b'IL'), (b'L', b'Chicago'),
+ (b'O', b'Testing'), (b'CN', b'Testing Root CA')])
+
+ def test_set_issuer_wrong_args(self):
+ """
+ `X509.set_issuer` raises a `TypeError` if called with an argument not
+ of type `X509Name`.
+ """
+ cert = X509()
+ with pytest.raises(TypeError):
+ cert.set_issuer(None)
+
+ def test_set_issuer(self):
+ """
+ `X509.set_issuer` changes the issuer of the certificate to the
+ one passed in.
+ """
+ cert = X509()
+ name = cert.get_issuer()
+ name.C = 'AU'
+ name.OU = 'Unit Tests'
+ cert.set_issuer(name)
+ assert (
+ cert.get_issuer().get_components() ==
+ [(b'C', b'AU'), (b'OU', b'Unit Tests')])
+
+ def test_get_pubkey_uninitialized(self):
+ """
+ When called on a certificate with no public key, `X509.get_pubkey`
+ raises `OpenSSL.crypto.Error`.
+ """
+ cert = X509()
+ with pytest.raises(Error):
+ cert.get_pubkey()
+
+ def test_set_pubkey_wrong_type(self):
+ """
+ `X509.set_pubkey` raises `TypeError` when given an object of the
+ wrong type.
+ """
+ cert = X509()
+ with pytest.raises(TypeError):
+ cert.set_pubkey(object())
+
+ def test_subject_name_hash(self):
+ """
+ `X509.subject_name_hash` returns the hash of the certificate's
+ subject name.
+ """
+ cert = load_certificate(FILETYPE_PEM, self.pemData)
+ assert cert.subject_name_hash() in [
+ 3350047874, # OpenSSL 0.9.8, MD5
+ 3278919224, # OpenSSL 1.0.0, SHA1
+ ]
+
+ def test_get_signature_algorithm(self):
+ """
+ `X509.get_signature_algorithm` returns a string which means
+ the algorithm used to sign the certificate.
+ """
+ cert = load_certificate(FILETYPE_PEM, self.pemData)
+ assert b"sha1WithRSAEncryption" == cert.get_signature_algorithm()
+
+ def test_get_undefined_signature_algorithm(self):
+ """
+ `X509.get_signature_algorithm` raises `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 = b"""\
+-----BEGIN CERTIFICATE-----
+MIIC/zCCAmigAwIBAgIBATAGBgJ8BQUAMHsxCzAJBgNVBAYTAlNHMREwDwYDVQQK
+EwhNMkNyeXB0bzEUMBIGA1UECxMLTTJDcnlwdG8gQ0ExJDAiBgNVBAMTG00yQ3J5
+cHRvIENlcnRpZmljYXRlIE1hc3RlcjEdMBsGCSqGSIb3DQEJARYObmdwc0Bwb3N0
+MS5jb20wHhcNMDAwOTEwMDk1MTMwWhcNMDIwOTEwMDk1MTMwWjBTMQswCQYDVQQG
+EwJTRzERMA8GA1UEChMITTJDcnlwdG8xEjAQBgNVBAMTCWxvY2FsaG9zdDEdMBsG
+CSqGSIb3DQEJARYObmdwc0Bwb3N0MS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBI
+AkEArL57d26W9fNXvOhNlZzlPOACmvwOZ5AdNgLzJ1/MfsQQJ7hHVeHmTAjM664V
++fXvwUGJLziCeBo1ysWLRnl8CQIDAQABo4IBBDCCAQAwCQYDVR0TBAIwADAsBglg
+hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O
+BBYEFM+EgpK+eyZiwFU1aOPSbczbPSpVMIGlBgNVHSMEgZ0wgZqAFPuHI2nrnDqT
+FeXFvylRT/7tKDgBoX+kfTB7MQswCQYDVQQGEwJTRzERMA8GA1UEChMITTJDcnlw
+dG8xFDASBgNVBAsTC00yQ3J5cHRvIENBMSQwIgYDVQQDExtNMkNyeXB0byBDZXJ0
+aWZpY2F0ZSBNYXN0ZXIxHTAbBgkqhkiG9w0BCQEWDm5ncHNAcG9zdDEuY29tggEA
+MA0GCSqGSIb3DQEBBAUAA4GBADv8KpPo+gfJxN2ERK1Y1l17sz/ZhzoGgm5XCdbx
+jEY7xKfpQngV599k1xhl11IMqizDwu0855agrckg2MCTmOI9DZzDD77tAYb+Dk0O
+PEVk0Mk/V0aIsDE9bolfCi/i/QWZ3N8s5nTWMNyBBBmoSliWCm4jkkRZRD0ejgTN
+tgI5
+-----END CERTIFICATE-----
+"""
+ cert = load_certificate(FILETYPE_PEM, certPEM)
+ with pytest.raises(ValueError):
+ cert.get_signature_algorithm()
+
+ def test_sign_bad_pubkey_type(self):
+ """
+ `X509.sign` raises `TypeError` when called with the wrong type.
+ """
+ cert = X509()
+ with pytest.raises(TypeError):
+ cert.sign(object(), b"sha256")
+
+ def test_convert_from_cryptography(self):
+ crypto_cert = x509.load_pem_x509_certificate(
+ intermediate_cert_pem, backend
+ )
+ cert = X509.from_cryptography(crypto_cert)
+
+ assert isinstance(cert, X509)
+ assert cert.get_version() == crypto_cert.version.value
+
+ def test_convert_from_cryptography_unsupported_type(self):
+ with pytest.raises(TypeError):
+ X509.from_cryptography(object())
+
+ def test_convert_to_cryptography_key(self):
+ cert = load_certificate(FILETYPE_PEM, intermediate_cert_pem)
+ crypto_cert = cert.to_cryptography()
+
+ assert isinstance(crypto_cert, x509.Certificate)
+ assert crypto_cert.version.value == cert.get_version()
+
+
+class TestX509Store(object):
+ """
+ Test for `OpenSSL.crypto.X509Store`.
+ """
+
+ def test_type(self):
+ """
+ `X509Store` is a type object.
+ """
+ assert X509Store is X509StoreType
+ assert is_consistent_type(X509Store, 'X509Store')
+
+ def test_add_cert(self):
+ """
+ `X509Store.add_cert` adds a `X509` instance to the certificate store.
+ """
+ cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
+ store = X509Store()
+ store.add_cert(cert)
+
+ @pytest.mark.parametrize('cert', [None, 1.0, 'cert', object()])
+ def test_add_cert_wrong_args(self, cert):
+ """
+ `X509Store.add_cert` raises `TypeError` if passed a non-X509 object
+ as its first argument.
+ """
+ store = X509Store()
+ with pytest.raises(TypeError):
+ store.add_cert(cert)
+
+ def test_add_cert_accepts_duplicate(self):
+ """
+ `X509Store.add_cert` doesn't raise `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)
+ store.add_cert(cert)
+
+
+class TestPKCS12(object):
+ """
+ Test for `OpenSSL.crypto.PKCS12` and `OpenSSL.crypto.load_pkcs12`.
+ """
+ pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
+
+ def test_type(self):
+ """
+ `PKCS12Type` is a type object.
+ """
+ assert PKCS12 is PKCS12Type
+ assert is_consistent_type(PKCS12, 'PKCS12')
+
+ def test_empty_construction(self):
+ """
+ `PKCS12` returns a new instance of `PKCS12` with no certificate,
+ private key, CA certificates, or friendly name.
+ """
+ p12 = PKCS12()
+ assert None is p12.get_certificate()
+ assert None is p12.get_privatekey()
+ assert None is p12.get_ca_certificates()
+ assert None is p12.get_friendlyname()
+
+ def test_type_errors(self):
+ """
+ The `PKCS12` setter functions (`set_certificate`, `set_privatekey`,
+ `set_ca_certificates`, and `set_friendlyname`) raise `TypeError`
+ when passed objects of types other than those expected.
+ """
+ p12 = PKCS12()
+ for bad_arg in [3, PKey(), X509]:
+ with pytest.raises(TypeError):
+ p12.set_certificate(bad_arg)
+ for bad_arg in [3, 'legbone', X509()]:
+ with pytest.raises(TypeError):
+ p12.set_privatekey(bad_arg)
+ for bad_arg in [3, X509(), (3, 4), (PKey(),)]:
+ with pytest.raises(TypeError):
+ p12.set_ca_certificates(bad_arg)
+ for bad_arg in [6, ('foo', 'bar')]:
+ with pytest.raises(TypeError):
+ p12.set_friendlyname(bad_arg)
+
+ def test_key_only(self):
+ """
+ A `PKCS12` with only a private key can be exported using
+ `PKCS12.export` and loaded again using `load_pkcs12`.
+ """
+ passwd = b"blah"
+ p12 = PKCS12()
+ pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ p12.set_privatekey(pkey)
+ assert None is p12.get_certificate()
+ assert pkey == p12.get_privatekey()
+ try:
+ dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3)
+ except Error:
+ # Some versions of OpenSSL will throw an exception
+ # for this nearly useless PKCS12 we tried to generate:
+ # [('PKCS12 routines', 'PKCS12_create', 'invalid null argument')]
+ return
+ p12 = load_pkcs12(dumped_p12, passwd)
+ assert None is p12.get_ca_certificates()
+ assert None is p12.get_certificate()
+
+ # OpenSSL fails to bring the key back to us. So sad. Perhaps in the
+ # future this will be improved.
+ assert isinstance(p12.get_privatekey(), (PKey, type(None)))
+
+ def test_cert_only(self):
+ """
+ A `PKCS12` with only a certificate can be exported using
+ `PKCS12.export` and loaded again using `load_pkcs12`.
+ """
+ passwd = b"blah"
+ p12 = PKCS12()
+ cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
+ p12.set_certificate(cert)
+ assert cert == p12.get_certificate()
+ assert None is p12.get_privatekey()
+ try:
+ dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3)
+ except Error:
+ # Some versions of OpenSSL will throw an exception
+ # for this nearly useless PKCS12 we tried to generate:
+ # [('PKCS12 routines', 'PKCS12_create', 'invalid null argument')]
+ return
+ p12 = load_pkcs12(dumped_p12, passwd)
+ assert None is p12.get_privatekey()
+
+ # OpenSSL fails to bring the cert back to us. Groany mcgroan.
+ assert isinstance(p12.get_certificate(), (X509, type(None)))
+
+ # Oh ho. It puts the certificate into the ca certificates list, in
+ # fact. Totally bogus, I would think. Nevertheless, let's exploit
+ # that to check to see if it reconstructed the certificate we expected
+ # it to. At some point, hopefully this will change so that
+ # p12.get_certificate() is actually what returns the loaded
+ # certificate.
+ assert (
+ cleartextCertificatePEM ==
+ dump_certificate(FILETYPE_PEM, p12.get_ca_certificates()[0]))
+
+ def gen_pkcs12(self, cert_pem=None, key_pem=None, ca_pem=None,
+ friendly_name=None):
+ """
+ Generate a PKCS12 object with components from PEM. Verify that the set
+ functions return None.
+ """
+ p12 = PKCS12()
+ if cert_pem:
+ ret = p12.set_certificate(load_certificate(FILETYPE_PEM, cert_pem))
+ assert ret is None
+ if key_pem:
+ ret = p12.set_privatekey(load_privatekey(FILETYPE_PEM, key_pem))
+ assert ret is None
+ if ca_pem:
+ ret = p12.set_ca_certificates(
+ (load_certificate(FILETYPE_PEM, ca_pem),)
+ )
+ assert ret is None
+ if friendly_name:
+ ret = p12.set_friendlyname(friendly_name)
+ assert ret is None
+ return p12
+
+ 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
+ PKCS12 string.
+ """
+ if key:
+ recovered_key = _runopenssl(
+ p12_str, b"pkcs12", b"-nocerts", b"-nodes", b"-passin",
+ b"pass:" + passwd, *extra)
+ assert recovered_key[-len(key):] == key
+ if cert:
+ recovered_cert = _runopenssl(
+ p12_str, b"pkcs12", b"-clcerts", b"-nodes", b"-passin",
+ b"pass:" + passwd, b"-nokeys", *extra)
+ assert recovered_cert[-len(cert):] == cert
+ if ca:
+ recovered_cert = _runopenssl(
+ p12_str, b"pkcs12", b"-cacerts", b"-nodes", b"-passin",
+ b"pass:" + passwd, b"-nokeys", *extra)
+ assert recovered_cert[-len(ca):] == ca
+
+ def verify_pkcs12_container(self, p12):
+ """
+ Verify that the PKCS#12 container contains the correct client
+ certificate and private key.
+
+ :param p12: The PKCS12 instance to verify.
+ :type p12: `PKCS12`
+ """
+ cert_pem = dump_certificate(FILETYPE_PEM, p12.get_certificate())
+ key_pem = dump_privatekey(FILETYPE_PEM, p12.get_privatekey())
+ assert (
+ (client_cert_pem, client_key_pem, None) ==
+ (cert_pem, key_pem, p12.get_ca_certificates()))
+
+ def test_load_pkcs12(self):
+ """
+ A PKCS12 string generated using the openssl command line can be loaded
+ with `load_pkcs12` and its components extracted and examined.
+ """
+ passwd = b"whatever"
+ pem = client_key_pem + client_cert_pem
+ p12_str = _runopenssl(
+ pem,
+ b"pkcs12",
+ b"-export",
+ b"-clcerts",
+ b"-passout",
+ b"pass:" + passwd
+ )
+ p12 = load_pkcs12(p12_str, passphrase=passwd)
+ self.verify_pkcs12_container(p12)
+
+ def test_load_pkcs12_text_passphrase(self):
+ """
+ A PKCS12 string generated using the openssl command line can be loaded
+ with `load_pkcs12` and its components extracted and examined.
+ Using text as passphrase instead of bytes. DeprecationWarning expected.
+ """
+ pem = client_key_pem + client_cert_pem
+ passwd = b"whatever"
+ p12_str = _runopenssl(pem, b"pkcs12", b"-export", b"-clcerts",
+ b"-passout", b"pass:" + passwd)
+ with pytest.warns(DeprecationWarning) as w:
+ simplefilter("always")
+ p12 = load_pkcs12(p12_str, passphrase=b"whatever".decode("ascii"))
+ assert (
+ "{0} for passphrase is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ ) == str(w[-1].message))
+
+ self.verify_pkcs12_container(p12)
+
+ def test_load_pkcs12_no_passphrase(self):
+ """
+ A PKCS12 string generated using openssl command line can be loaded with
+ `load_pkcs12` without a passphrase and its components extracted
+ and examined.
+ """
+ pem = client_key_pem + client_cert_pem
+ p12_str = _runopenssl(
+ pem, b"pkcs12", b"-export", b"-clcerts", b"-passout", b"pass:")
+ p12 = load_pkcs12(p12_str)
+ self.verify_pkcs12_container(p12)
+
+ def _dump_and_load(self, dump_passphrase, load_passphrase):
+ """
+ A helper method to dump and load a PKCS12 object.
+ """
+ p12 = self.gen_pkcs12(client_cert_pem, client_key_pem)
+ dumped_p12 = p12.export(passphrase=dump_passphrase, iter=2, maciter=3)
+ return load_pkcs12(dumped_p12, passphrase=load_passphrase)
+
+ def test_load_pkcs12_null_passphrase_load_empty(self):
+ """
+ A PKCS12 string can be dumped with a null passphrase, loaded with an
+ empty passphrase with `load_pkcs12`, and its components
+ extracted and examined.
+ """
+ self.verify_pkcs12_container(
+ self._dump_and_load(dump_passphrase=None, load_passphrase=b''))
+
+ def test_load_pkcs12_null_passphrase_load_null(self):
+ """
+ A PKCS12 string can be dumped with a null passphrase, loaded with a
+ null passphrase with `load_pkcs12`, and its components
+ extracted and examined.
+ """
+ self.verify_pkcs12_container(
+ self._dump_and_load(dump_passphrase=None, load_passphrase=None))
+
+ def test_load_pkcs12_empty_passphrase_load_empty(self):
+ """
+ A PKCS12 string can be dumped with an empty passphrase, loaded with an
+ empty passphrase with `load_pkcs12`, and its components
+ extracted and examined.
+ """
+ self.verify_pkcs12_container(
+ self._dump_and_load(dump_passphrase=b'', load_passphrase=b''))
+
+ def test_load_pkcs12_empty_passphrase_load_null(self):
+ """
+ A PKCS12 string can be dumped with an empty passphrase, loaded with a
+ null passphrase with `load_pkcs12`, and its components
+ extracted and examined.
+ """
+ self.verify_pkcs12_container(
+ self._dump_and_load(dump_passphrase=b'', load_passphrase=None))
+
+ def test_load_pkcs12_garbage(self):
+ """
+ `load_pkcs12` raises `OpenSSL.crypto.Error` when passed
+ a string which is not a PKCS12 dump.
+ """
+ passwd = 'whatever'
+ with pytest.raises(Error) as err:
+ load_pkcs12(b'fruit loops', passwd)
+ assert err.value.args[0][0][0] == 'asn1 encoding routines'
+ assert len(err.value.args[0][0]) == 3
+
+ def test_replace(self):
+ """
+ `PKCS12.set_certificate` replaces the certificate in a PKCS12
+ cluster. `PKCS12.set_privatekey` replaces the private key.
+ `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))
+ p12.set_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
+ root_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ client_cert = load_certificate(FILETYPE_PEM, client_cert_pem)
+ p12.set_ca_certificates([root_cert]) # not a tuple
+ assert 1 == len(p12.get_ca_certificates())
+ assert root_cert == p12.get_ca_certificates()[0]
+ p12.set_ca_certificates([client_cert, root_cert])
+ assert 2 == len(p12.get_ca_certificates())
+ assert client_cert == p12.get_ca_certificates()[0]
+ assert root_cert == p12.get_ca_certificates()[1]
+
+ def test_friendly_name(self):
+ """
+ The *friendlyName* of a PKCS12 can be set and retrieved via
+ `PKCS12.get_friendlyname` and `PKCS12_set_friendlyname`, and a
+ `PKCS12` with a friendly name set can be dumped with `PKCS12.export`.
+ """
+ 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)
+ assert p12.get_friendlyname() == friendly_name
+ dumped_p12 = p12.export(passphrase=passwd, iter=2, maciter=3)
+ reloaded_p12 = load_pkcs12(dumped_p12, passwd)
+ assert p12.get_friendlyname() == reloaded_p12.get_friendlyname()
+ # We would use the openssl program to confirm the friendly
+ # name, but it is not possible. The pkcs12 command
+ # does not store the friendly name in the cert's
+ # alias, which we could then extract.
+ self.check_recovery(
+ dumped_p12, key=server_key_pem, cert=server_cert_pem,
+ ca=root_cert_pem, passwd=passwd)
+
+ def test_various_empty_passphrases(self):
+ """
+ Test that missing, None, and '' passphrases are identical for PKCS12
+ export.
+ """
+ p12 = self.gen_pkcs12(client_cert_pem, client_key_pem, root_cert_pem)
+ 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)
+ for dumped_p12 in [dumped_p12_empty, dumped_p12_none, dumped_p12_nopw]:
+ self.check_recovery(
+ dumped_p12, key=client_key_pem, cert=client_cert_pem,
+ ca=root_cert_pem, passwd=passwd)
+
+ def test_removing_ca_cert(self):
+ """
+ Passing `None` to `PKCS12.set_ca_certificates` removes all CA
+ certificates.
+ """
+ p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem)
+ p12.set_ca_certificates(None)
+ assert None is p12.get_ca_certificates()
+
+ def test_export_without_mac(self):
+ """
+ Exporting a PKCS12 with a `maciter` of `-1` excludes the MAC entirely.
+ """
+ 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=(b"-nomacver",))
+
+ def test_load_without_mac(self):
+ """
+ Loading a PKCS12 without a MAC does something other than crash.
+ """
+ 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:
+ recovered_p12 = load_pkcs12(dumped_p12, passwd)
+ # The person who generated this PCKS12 should be flogged,
+ # or better yet we should have a means to determine
+ # whether a PCKS12 had a MAC that was verified.
+ # Anyway, libopenssl chooses to allow it, so the
+ # pyopenssl binding does as well.
+ assert isinstance(recovered_p12, PKCS12)
+ except Error:
+ # Failing here with an exception is preferred as some openssl
+ # versions do.
+ pass
+
+ def test_zero_len_list_for_ca(self):
+ """
+ A PKCS12 with an empty CA certificates list can be exported.
+ """
+ passwd = b'Hobie 18'
+ p12 = self.gen_pkcs12(server_cert_pem, server_key_pem)
+ p12.set_ca_certificates([])
+ assert () == 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 `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=b"")
+
+ def test_export_without_bytes(self):
+ """
+ Test `PKCS12.export` with text not bytes as passphrase
+ """
+ p12 = self.gen_pkcs12(server_cert_pem, server_key_pem, root_cert_pem)
+
+ with pytest.warns(DeprecationWarning) as w:
+ simplefilter("always")
+ dumped_p12 = p12.export(passphrase=b"randomtext".decode("ascii"))
+ assert (
+ "{0} for passphrase is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ ) == str(w[-1].message))
+ self.check_recovery(
+ dumped_p12,
+ key=server_key_pem,
+ cert=server_cert_pem,
+ passwd=b"randomtext"
+ )
+
+ def test_key_cert_mismatch(self):
+ """
+ `PKCS12.export` raises an exception when a key and certificate
+ mismatch.
+ """
+ p12 = self.gen_pkcs12(server_cert_pem, client_key_pem, root_cert_pem)
+ with pytest.raises(Error):
+ p12.export()
+
+
+def _runopenssl(pem, *args):
+ """
+ Run the command line openssl tool with the given arguments and write
+ the given PEM to its stdin. Not safe for quotes.
+ """
+ proc = Popen([b"openssl"] + list(args), stdin=PIPE, stdout=PIPE)
+ proc.stdin.write(pem)
+ proc.stdin.close()
+ output = proc.stdout.read()
+ proc.stdout.close()
+ proc.wait()
+ return output
+
+
+class TestLoadPublicKey(object):
+ """
+ Tests for :func:`load_publickey`.
+ """
+ def test_loading_works(self):
+ """
+ load_publickey loads public keys and sets correct attributes.
+ """
+ key = load_publickey(FILETYPE_PEM, cleartextPublicKeyPEM)
+
+ assert True is key._only_public
+ assert 2048 == key.bits()
+ assert TYPE_RSA == key.type()
+
+ def test_invalid_type(self):
+ """
+ load_publickey doesn't support FILETYPE_TEXT.
+ """
+ with pytest.raises(ValueError):
+ load_publickey(FILETYPE_TEXT, cleartextPublicKeyPEM)
+
+ def test_invalid_key_format(self):
+ """
+ load_publickey explodes on incorrect keys.
+ """
+ with pytest.raises(Error):
+ load_publickey(FILETYPE_ASN1, cleartextPublicKeyPEM)
+
+ def test_tolerates_unicode_strings(self):
+ """
+ load_publickey works with text strings, not just bytes.
+ """
+ serialized = cleartextPublicKeyPEM.decode('ascii')
+ key = load_publickey(FILETYPE_PEM, serialized)
+ dumped_pem = dump_publickey(FILETYPE_PEM, key)
+
+ assert dumped_pem == cleartextPublicKeyPEM
+
+
+class TestFunction(object):
+ """
+ Tests for free-functions in the `OpenSSL.crypto` module.
+ """
+
+ def test_load_privatekey_invalid_format(self):
+ """
+ `load_privatekey` raises `ValueError` if passed an unknown filetype.
+ """
+ with pytest.raises(ValueError):
+ load_privatekey(100, root_key_pem)
+
+ def test_load_privatekey_invalid_passphrase_type(self):
+ """
+ `load_privatekey` raises `TypeError` if passed a passphrase that is
+ neither a `str` nor a callable.
+ """
+ with pytest.raises(TypeError):
+ load_privatekey(
+ FILETYPE_PEM, encryptedPrivateKeyPEMPassphrase, object())
+
+ def test_load_privatekey_wrongPassphrase(self):
+ """
+ `load_privatekey` raises `OpenSSL.crypto.Error` when it is passed an
+ encrypted PEM and an incorrect passphrase.
+ """
+ with pytest.raises(Error) as err:
+ load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, b"quack")
+ assert err.value.args[0] != []
+
+ def test_load_privatekey_passphraseWrongType(self):
+ """
+ `load_privatekey` raises `ValueError` when it is passeda 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)
+ with pytest.raises(ValueError):
+ load_privatekey(FILETYPE_ASN1, blob, "secret")
+
+ def test_load_privatekey_passphrase(self):
+ """
+ `load_privatekey` can create a `PKey` object from an encrypted PEM
+ string if given the passphrase.
+ """
+ key = load_privatekey(
+ FILETYPE_PEM, encryptedPrivateKeyPEM,
+ encryptedPrivateKeyPEMPassphrase)
+ assert isinstance(key, PKeyType)
+
+ def test_load_privatekey_passphrase_exception(self):
+ """
+ If the passphrase callback raises an exception, that exception is
+ raised by `load_privatekey`.
+ """
+ def cb(ignored):
+ raise ArithmeticError
+
+ with pytest.raises(ArithmeticError):
+ load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
+
+ def test_load_privatekey_wrongPassphraseCallback(self):
+ """
+ `load_privatekey` raises `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 b"quack"
+ with pytest.raises(Error) as err:
+ load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
+ assert called
+ assert err.value.args[0] != []
+
+ def test_load_privatekey_passphraseCallback(self):
+ """
+ `load_privatekey` can create a `PKey` object from an encrypted PEM
+ string if given a passphrase callback which returns the correct
+ password.
+ """
+ called = []
+
+ def cb(writing):
+ called.append(writing)
+ return encryptedPrivateKeyPEMPassphrase
+ key = load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
+ assert isinstance(key, PKeyType)
+ assert called == [False]
+
+ def test_load_privatekey_passphrase_wrong_return_type(self):
+ """
+ `load_privatekey` raises `ValueError` if the passphrase callback
+ returns something other than a byte string.
+ """
+ with pytest.raises(ValueError):
+ load_privatekey(
+ FILETYPE_PEM, encryptedPrivateKeyPEM, lambda *args: 3)
+
+ def test_dump_privatekey_wrong_args(self):
+ """
+ `dump_privatekey` raises `TypeError` if called with a `cipher`
+ argument but no `passphrase` argument.
+ """
+ key = PKey()
+ key.generate_key(TYPE_RSA, 512)
+ with pytest.raises(TypeError):
+ dump_privatekey(FILETYPE_PEM, key, cipher=GOOD_CIPHER)
+
+ def test_dump_privatekey_not_rsa_key(self):
+ """
+ `dump_privatekey` raises `TypeError` if called with a key that is
+ not RSA.
+ """
+ key = PKey()
+ key.generate_key(TYPE_DSA, 512)
+ with pytest.raises(TypeError):
+ dump_privatekey(FILETYPE_TEXT, key)
+
+ def test_dump_privatekey_invalid_pkey(self):
+ with pytest.raises(TypeError):
+ dump_privatekey(FILETYPE_TEXT, object())
+
+ def test_dump_privatekey_unknown_cipher(self):
+ """
+ `dump_privatekey` raises `ValueError` if called with an unrecognized
+ cipher name.
+ """
+ key = PKey()
+ key.generate_key(TYPE_RSA, 512)
+ with pytest.raises(ValueError):
+ dump_privatekey(FILETYPE_PEM, key, BAD_CIPHER, "passphrase")
+
+ def test_dump_privatekey_invalid_passphrase_type(self):
+ """
+ `dump_privatekey` raises `TypeError` if called with a passphrase which
+ is neither a `str` nor a callable.
+ """
+ key = PKey()
+ key.generate_key(TYPE_RSA, 512)
+ with pytest.raises(TypeError):
+ dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, object())
+
+ def test_dump_privatekey_invalid_filetype(self):
+ """
+ `dump_privatekey` raises `ValueError` if called with an unrecognized
+ filetype.
+ """
+ key = PKey()
+ key.generate_key(TYPE_RSA, 512)
+ with pytest.raises(ValueError):
+ dump_privatekey(100, key)
+
+ def test_load_privatekey_passphrase_callback_length(self):
+ """
+ `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
+
+ with pytest.raises(ValueError):
+ load_privatekey(FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
+
+ def test_dump_privatekey_passphrase(self):
+ """
+ `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, GOOD_CIPHER, passphrase)
+ assert isinstance(pem, binary_type)
+ loadedKey = load_privatekey(FILETYPE_PEM, pem, passphrase)
+ assert isinstance(loadedKey, PKeyType)
+ assert loadedKey.type() == key.type()
+ assert loadedKey.bits() == key.bits()
+
+ def test_dump_privatekey_passphrase_wrong_type(self):
+ """
+ `dump_privatekey` raises `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)
+ with pytest.raises(ValueError):
+ dump_privatekey(FILETYPE_ASN1, key, GOOD_CIPHER, "secret")
+
+ def test_dump_certificate(self):
+ """
+ `dump_certificate` writes PEM, DER, and text.
+ """
+ pemData = cleartextCertificatePEM + cleartextPrivateKeyPEM
+ cert = load_certificate(FILETYPE_PEM, pemData)
+ dumped_pem = dump_certificate(FILETYPE_PEM, cert)
+ assert dumped_pem == cleartextCertificatePEM
+ dumped_der = dump_certificate(FILETYPE_ASN1, cert)
+ good_der = _runopenssl(dumped_pem, b"x509", b"-outform", b"DER")
+ assert dumped_der == good_der
+ cert2 = load_certificate(FILETYPE_ASN1, dumped_der)
+ dumped_pem2 = dump_certificate(FILETYPE_PEM, cert2)
+ assert dumped_pem2 == cleartextCertificatePEM
+ dumped_text = dump_certificate(FILETYPE_TEXT, cert)
+ good_text = _runopenssl(
+ dumped_pem, b"x509", b"-noout", b"-text", b"-nameopt", b"")
+ assert dumped_text == good_text
+
+ def test_dump_certificate_bad_type(self):
+ """
+ `dump_certificate` raises a `ValueError` if it's called with
+ a bad type.
+ """
+ cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
+ with pytest.raises(ValueError):
+ dump_certificate(object(), cert)
+
+ def test_dump_privatekey_pem(self):
+ """
+ `dump_privatekey` writes a PEM
+ """
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ assert key.check()
+ dumped_pem = dump_privatekey(FILETYPE_PEM, key)
+ assert dumped_pem == cleartextPrivateKeyPEM
+
+ def test_dump_privatekey_asn1(self):
+ """
+ `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, b"rsa", b"-outform", b"DER")
+ assert dumped_der == good_der
+ key2 = load_privatekey(FILETYPE_ASN1, dumped_der)
+ dumped_pem2 = dump_privatekey(FILETYPE_PEM, key2)
+ assert dumped_pem2 == cleartextPrivateKeyPEM
+
+ def test_dump_privatekey_text(self):
+ """
+ `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, b"rsa", b"-noout", b"-text")
+ assert dumped_text == good_text
+
+ def test_dump_publickey_pem(self):
+ """
+ dump_publickey writes a PEM.
+ """
+ key = load_publickey(FILETYPE_PEM, cleartextPublicKeyPEM)
+ dumped_pem = dump_publickey(FILETYPE_PEM, key)
+ assert dumped_pem == cleartextPublicKeyPEM
+
+ def test_dump_publickey_asn1(self):
+ """
+ dump_publickey writes a DER.
+ """
+ key = load_publickey(FILETYPE_PEM, cleartextPublicKeyPEM)
+ dumped_der = dump_publickey(FILETYPE_ASN1, key)
+ key2 = load_publickey(FILETYPE_ASN1, dumped_der)
+ dumped_pem2 = dump_publickey(FILETYPE_PEM, key2)
+ assert dumped_pem2 == cleartextPublicKeyPEM
+
+ def test_dump_publickey_invalid_type(self):
+ """
+ dump_publickey doesn't support FILETYPE_TEXT.
+ """
+ key = load_publickey(FILETYPE_PEM, cleartextPublicKeyPEM)
+
+ with pytest.raises(ValueError):
+ dump_publickey(FILETYPE_TEXT, key)
+
+ def test_dump_certificate_request(self):
+ """
+ `dump_certificate_request` writes a PEM, DER, and text.
+ """
+ req = load_certificate_request(
+ FILETYPE_PEM, cleartextCertificateRequestPEM)
+ dumped_pem = dump_certificate_request(FILETYPE_PEM, req)
+ assert dumped_pem == cleartextCertificateRequestPEM
+ dumped_der = dump_certificate_request(FILETYPE_ASN1, req)
+ good_der = _runopenssl(dumped_pem, b"req", b"-outform", b"DER")
+ assert dumped_der == good_der
+ req2 = load_certificate_request(FILETYPE_ASN1, dumped_der)
+ dumped_pem2 = dump_certificate_request(FILETYPE_PEM, req2)
+ assert dumped_pem2 == cleartextCertificateRequestPEM
+ dumped_text = dump_certificate_request(FILETYPE_TEXT, req)
+ good_text = _runopenssl(
+ dumped_pem, b"req", b"-noout", b"-text", b"-nameopt", b"")
+ assert dumped_text == good_text
+ with pytest.raises(ValueError):
+ dump_certificate_request(100, req)
+
+ def test_dump_privatekey_passphrase_callback(self):
+ """
+ `dump_privatekey` writes an encrypted PEM when given a callback
+ which returns the correct passphrase.
+ """
+ passphrase = b"foo"
+ called = []
+
+ def cb(writing):
+ called.append(writing)
+ return passphrase
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ pem = dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, cb)
+ assert isinstance(pem, binary_type)
+ assert called == [True]
+ loadedKey = load_privatekey(FILETYPE_PEM, pem, passphrase)
+ assert isinstance(loadedKey, PKeyType)
+ assert loadedKey.type() == key.type()
+ assert loadedKey.bits() == key.bits()
+
+ def test_dump_privatekey_passphrase_exception(self):
+ """
+ `dump_privatekey` should not overwrite the exception raised
+ by the passphrase callback.
+ """
+ def cb(ignored):
+ raise ArithmeticError
+
+ key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+ with pytest.raises(ArithmeticError):
+ dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, cb)
+
+ def test_dump_privatekey_passphraseCallbackLength(self):
+ """
+ `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)
+ with pytest.raises(ValueError):
+ dump_privatekey(FILETYPE_PEM, key, GOOD_CIPHER, cb)
+
+ def test_load_pkcs7_data_pem(self):
+ """
+ `load_pkcs7_data` accepts a PKCS#7 string and returns an instance of
+ `PKCS`.
+ """
+ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
+ assert isinstance(pkcs7, PKCS7)
+
+ def test_load_pkcs7_data_asn1(self):
+ """
+ `load_pkcs7_data` accepts a bytes containing ASN1 data representing
+ PKCS#7 and returns an instance of `PKCS7`.
+ """
+ pkcs7 = load_pkcs7_data(FILETYPE_ASN1, pkcs7DataASN1)
+ assert isinstance(pkcs7, PKCS7)
+
+ def test_load_pkcs7_data_invalid(self):
+ """
+ If the data passed to `load_pkcs7_data` is invalid, `Error` is raised.
+ """
+ with pytest.raises(Error):
+ load_pkcs7_data(FILETYPE_PEM, b"foo")
+
+ def test_load_pkcs7_type_invalid(self):
+ """
+ If the type passed to `load_pkcs7_data`, `ValueError` is raised.
+ """
+ with pytest.raises(ValueError):
+ load_pkcs7_data(object(), b"foo")
+
+
+class TestLoadCertificate(object):
+ """
+ Tests for `load_certificate_request`.
+ """
+
+ def test_bad_file_type(self):
+ """
+ If the file type passed to `load_certificate_request` is neither
+ `FILETYPE_PEM` nor `FILETYPE_ASN1` then `ValueError` is raised.
+ """
+ with pytest.raises(ValueError):
+ load_certificate_request(object(), b"")
+ with pytest.raises(ValueError):
+ load_certificate(object(), b"")
+
+ def test_bad_certificate(self):
+ """
+ If the bytes passed to `load_certificate` are not a valid certificate,
+ an exception is raised.
+ """
+ with pytest.raises(Error):
+ load_certificate(FILETYPE_ASN1, b"lol")
+
+
+class TestPKCS7(object):
+ """
+ Tests for `PKCS7`.
+ """
+
+ def test_type(self):
+ """
+ `PKCS7` is a type object.
+ """
+ assert isinstance(PKCS7, type)
+ assert PKCS7Type.__name__ == 'PKCS7'
+ assert PKCS7 is PKCS7Type
+
+ def test_type_is_signed(self):
+ """
+ `PKCS7.type_is_signed` returns `True` if the PKCS7 object is of
+ the type *signed*.
+ """
+ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
+ assert pkcs7.type_is_signed()
+
+ def test_type_is_enveloped(self):
+ """
+ `PKCS7.type_is_enveloped` returns `False` if the PKCS7 object is not
+ of the type *enveloped*.
+ """
+ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
+ assert not pkcs7.type_is_enveloped()
+
+ def test_type_is_signed_and_enveloped(self):
+ """
+ `PKCS7.type_is_signedAndEnveloped` returns `False`
+ if the PKCS7 object is not of the type *signed and enveloped*.
+ """
+ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
+ assert not pkcs7.type_is_signedAndEnveloped()
+
+ def test_type_is_data(self):
+ """
+ `PKCS7.type_is_data` returns `False` if the PKCS7 object is not of
+ the type data.
+ """
+ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
+ assert not pkcs7.type_is_data()
+
+ def test_get_type_name(self):
+ """
+ `PKCS7.get_type_name` returns a `str` giving the
+ type name.
+ """
+ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
+ assert pkcs7.get_type_name() == b'pkcs7-signedData'
+
+ def test_attribute(self):
+ """
+ If an attribute other than one of the methods tested here is accessed
+ on an instance of `PKCS7`, `AttributeError` is raised.
+ """
+ pkcs7 = load_pkcs7_data(FILETYPE_PEM, pkcs7Data)
+ with pytest.raises(AttributeError):
+ pkcs7.foo
+
+
+class TestNetscapeSPKI(_PKeyInteractionTestsMixin):
+ """
+ Tests for `OpenSSL.crypto.NetscapeSPKI`.
+ """
+
+ def signable(self):
+ """
+ Return a new `NetscapeSPKI` for use with signing tests.
+ """
+ return NetscapeSPKI()
+
+ def test_type(self):
+ """
+ `NetscapeSPKI` and `NetscapeSPKIType` refer to the same type object
+ and can be used to create instances of that type.
+ """
+ assert NetscapeSPKI is NetscapeSPKIType
+ assert is_consistent_type(NetscapeSPKI, 'NetscapeSPKI')
+
+ def test_construction(self):
+ """
+ `NetscapeSPKI` returns an instance of `NetscapeSPKIType`.
+ """
+ nspki = NetscapeSPKI()
+ assert isinstance(nspki, NetscapeSPKIType)
+
+ def test_invalid_attribute(self):
+ """
+ Accessing a non-existent attribute of a `NetscapeSPKI` instance
+ causes an `AttributeError` to be raised.
+ """
+ nspki = NetscapeSPKI()
+ with pytest.raises(AttributeError):
+ nspki.foo
+
+ def test_b64_encode(self):
+ """
+ `NetscapeSPKI.b64_encode` encodes the certificate to a base64 blob.
+ """
+ nspki = NetscapeSPKI()
+ blob = nspki.b64_encode()
+ assert isinstance(blob, binary_type)
+
+
+class TestRevoked(object):
+ """
+ Tests for `OpenSSL.crypto.Revoked`.
+ """
+ def test_ignores_unsupported_revoked_cert_extension_get_reason(self):
+ """
+ The get_reason method on the Revoked class checks to see if the
+ extension is NID_crl_reason and should skip it otherwise. This test
+ loads a CRL with extensions it should ignore.
+ """
+ crl = load_crl(FILETYPE_PEM, crlDataUnsupportedExtension)
+ revoked = crl.get_revoked()
+ reason = revoked[1].get_reason()
+ assert reason == b'Unspecified'
+
+ def test_ignores_unsupported_revoked_cert_extension_set_new_reason(self):
+ crl = load_crl(FILETYPE_PEM, crlDataUnsupportedExtension)
+ revoked = crl.get_revoked()
+ revoked[1].set_reason(None)
+ reason = revoked[1].get_reason()
+ assert reason is None
+
+ def test_construction(self):
+ """
+ Confirm we can create `OpenSSL.crypto.Revoked`. Check that it is
+ empty.
+ """
+ revoked = Revoked()
+ assert isinstance(revoked, Revoked)
+ assert type(revoked) == Revoked
+ assert revoked.get_serial() == b'00'
+ assert revoked.get_rev_date() is None
+ assert revoked.get_reason() is None
+
+ def test_serial(self):
+ """
+ Confirm we can set and get serial numbers from
+ `OpenSSL.crypto.Revoked`. Confirm errors are handled with grace.
+ """
+ revoked = Revoked()
+ ret = revoked.set_serial(b'10b')
+ assert ret is None
+ ser = revoked.get_serial()
+ assert ser == b'010B'
+
+ revoked.set_serial(b'31ppp') # a type error would be nice
+ ser = revoked.get_serial()
+ assert ser == b'31'
+
+ with pytest.raises(ValueError):
+ revoked.set_serial(b'pqrst')
+ with pytest.raises(TypeError):
+ revoked.set_serial(100)
+
+ def test_date(self):
+ """
+ Confirm we can set and get revocation dates from
+ `OpenSSL.crypto.Revoked`. Confirm errors are handled with grace.
+ """
+ revoked = Revoked()
+ date = revoked.get_rev_date()
+ assert date is None
+
+ now = datetime.now().strftime("%Y%m%d%H%M%SZ").encode("ascii")
+ ret = revoked.set_rev_date(now)
+ assert ret is None
+ date = revoked.get_rev_date()
+ assert date == now
+
+ def test_reason(self):
+ """
+ Confirm we can set and get revocation reasons from
+ `OpenSSL.crypto.Revoked`. The "get" need to work as "set".
+ Likewise, each reason of all_reasons() must work.
+ """
+ revoked = Revoked()
+ for r in revoked.all_reasons():
+ for x in range(2):
+ ret = revoked.set_reason(r)
+ assert ret is None
+ reason = revoked.get_reason()
+ assert (
+ reason.lower().replace(b' ', b'') ==
+ r.lower().replace(b' ', b''))
+ r = reason # again with the resp of get
+
+ revoked.set_reason(None)
+ assert revoked.get_reason() is None
+
+ @pytest.mark.parametrize('reason', [object(), 1.0, u'foo'])
+ def test_set_reason_wrong_args(self, reason):
+ """
+ `Revoked.set_reason` raises `TypeError` if called with an argument
+ which is neither `None` nor a byte string.
+ """
+ revoked = Revoked()
+ with pytest.raises(TypeError):
+ revoked.set_reason(reason)
+
+ def test_set_reason_invalid_reason(self):
+ """
+ Calling `OpenSSL.crypto.Revoked.set_reason` with an argument which
+ isn't a valid reason results in `ValueError` being raised.
+ """
+ revoked = Revoked()
+ with pytest.raises(ValueError):
+ revoked.set_reason(b'blue')
+
+
+class TestCRL(object):
+ """
+ Tests for `OpenSSL.crypto.CRL`.
+ """
+ cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
+ pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+
+ root_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ root_key = load_privatekey(FILETYPE_PEM, root_key_pem)
+ intermediate_cert = load_certificate(FILETYPE_PEM, intermediate_cert_pem)
+ intermediate_key = load_privatekey(FILETYPE_PEM, intermediate_key_pem)
+ intermediate_server_cert = load_certificate(
+ FILETYPE_PEM, intermediate_server_cert_pem)
+ intermediate_server_key = load_privatekey(
+ FILETYPE_PEM, intermediate_server_key_pem)
+
+ def test_construction(self):
+ """
+ Confirm we can create `OpenSSL.crypto.CRL`. Check
+ that it is empty
+ """
+ crl = CRL()
+ assert isinstance(crl, CRL)
+ assert crl.get_revoked() is None
+
+ def _get_crl(self):
+ """
+ Get a new ``CRL`` with a revocation.
+ """
+ crl = CRL()
+ revoked = Revoked()
+ now = datetime.now().strftime("%Y%m%d%H%M%SZ").encode("ascii")
+ revoked.set_rev_date(now)
+ revoked.set_serial(b'3ab')
+ revoked.set_reason(b'sUpErSeDEd')
+ crl.add_revoked(revoked)
+ return crl
+
+ def test_export_pem(self):
+ """
+ If not passed a format, ``CRL.export`` returns a "PEM" format string
+ representing a serial number, a revoked reason, and certificate issuer
+ information.
+ """
+ # PEM format
+ dumped_crl = self._get_crl().export(
+ self.cert, self.pkey, days=20, digest=b"sha256"
+ )
+ crl = x509.load_pem_x509_crl(dumped_crl, backend)
+ revoked = crl.get_revoked_certificate_by_serial_number(0x03AB)
+ assert revoked is not None
+ assert crl.issuer == x509.Name([
+ x509.NameAttribute(x509.NameOID.COUNTRY_NAME, u"US"),
+ x509.NameAttribute(x509.NameOID.STATE_OR_PROVINCE_NAME, u"IL"),
+ x509.NameAttribute(x509.NameOID.LOCALITY_NAME, u"Chicago"),
+ x509.NameAttribute(x509.NameOID.ORGANIZATION_NAME, u"Testing"),
+ x509.NameAttribute(x509.NameOID.COMMON_NAME, u"Testing Root CA"),
+ ])
+
+ def test_export_der(self):
+ """
+ If passed ``FILETYPE_ASN1`` for the format, ``CRL.export`` returns a
+ "DER" format string representing a serial number, a revoked reason, and
+ certificate issuer information.
+ """
+ crl = self._get_crl()
+
+ # DER format
+ dumped_crl = self._get_crl().export(
+ self.cert, self.pkey, FILETYPE_ASN1, digest=b"md5"
+ )
+ crl = x509.load_der_x509_crl(dumped_crl, backend)
+ revoked = crl.get_revoked_certificate_by_serial_number(0x03AB)
+ assert revoked is not None
+ assert crl.issuer == x509.Name([
+ x509.NameAttribute(x509.NameOID.COUNTRY_NAME, u"US"),
+ x509.NameAttribute(x509.NameOID.STATE_OR_PROVINCE_NAME, u"IL"),
+ x509.NameAttribute(x509.NameOID.LOCALITY_NAME, u"Chicago"),
+ x509.NameAttribute(x509.NameOID.ORGANIZATION_NAME, u"Testing"),
+ x509.NameAttribute(x509.NameOID.COMMON_NAME, u"Testing Root CA"),
+ ])
+
+ # Flaky because we compare the output of running commands which sometimes
+ # varies by 1 second
+ @flaky.flaky
+ def test_export_text(self):
+ """
+ If passed ``FILETYPE_TEXT`` for the format, ``CRL.export`` returns a
+ text format string like the one produced by the openssl command line
+ tool.
+ """
+ crl = self._get_crl()
+
+ dumped_crl = crl.export(
+ self.cert, self.pkey, FILETYPE_ASN1, digest=b"md5"
+ )
+ text = _runopenssl(
+ dumped_crl, b"crl", b"-noout", b"-text", b"-inform", b"DER",
+ b"-nameopt", b""
+ )
+
+ # text format
+ dumped_text = crl.export(
+ self.cert, self.pkey, type=FILETYPE_TEXT, digest=b"md5"
+ )
+ assert text == dumped_text
+
+ def test_export_custom_digest(self):
+ """
+ If passed the name of a digest function, ``CRL.export`` uses a
+ signature algorithm based on that digest function.
+ """
+ crl = self._get_crl()
+ dumped_crl = crl.export(self.cert, self.pkey, digest=b"sha1")
+ text = _runopenssl(dumped_crl, b"crl", b"-noout", b"-text")
+ text.index(b'Signature Algorithm: sha1')
+
+ def test_export_md5_digest(self):
+ """
+ If passed md5 as the digest function, ``CRL.export`` uses md5 and does
+ not emit a deprecation warning.
+ """
+ crl = self._get_crl()
+ with pytest.warns(None) as catcher:
+ simplefilter("always")
+ assert 0 == len(catcher)
+ dumped_crl = crl.export(self.cert, self.pkey, digest=b"md5")
+ text = _runopenssl(dumped_crl, b"crl", b"-noout", b"-text")
+ text.index(b'Signature Algorithm: md5')
+
+ def test_export_default_digest(self):
+ """
+ If not passed the name of a digest function, ``CRL.export`` raises a
+ ``TypeError``.
+ """
+ crl = self._get_crl()
+ with pytest.raises(TypeError):
+ crl.export(self.cert, self.pkey)
+
+ def test_export_invalid(self):
+ """
+ If `CRL.export` is used with an uninitialized `X509` instance,
+ `OpenSSL.crypto.Error` is raised.
+ """
+ crl = CRL()
+ with pytest.raises(Error):
+ crl.export(X509(), PKey(), digest=b"sha256")
+
+ def test_add_revoked_keyword(self):
+ """
+ `OpenSSL.CRL.add_revoked` accepts its single argument as the
+ ``revoked`` keyword argument.
+ """
+ crl = CRL()
+ revoked = Revoked()
+ revoked.set_serial(b"01")
+ revoked.set_rev_date(b"20160310020145Z")
+ crl.add_revoked(revoked=revoked)
+ assert isinstance(crl.get_revoked()[0], Revoked)
+
+ def test_export_wrong_args(self):
+ """
+ Calling `OpenSSL.CRL.export` with arguments other than the certificate,
+ private key, integer file type, and integer number of days it
+ expects, results in a `TypeError` being raised.
+ """
+ crl = CRL()
+ with pytest.raises(TypeError):
+ crl.export(None, self.pkey, FILETYPE_PEM, 10)
+ with pytest.raises(TypeError):
+ crl.export(self.cert, None, FILETYPE_PEM, 10)
+ with pytest.raises(TypeError):
+ crl.export(self.cert, self.pkey, None, 10)
+ with pytest.raises(TypeError):
+ crl.export(self.cert, FILETYPE_PEM, None)
+
+ def test_export_unknown_filetype(self):
+ """
+ Calling `OpenSSL.CRL.export` with a file type other than
+ `FILETYPE_PEM`, `FILETYPE_ASN1`, or
+ `FILETYPE_TEXT` results in a `ValueError` being raised.
+ """
+ crl = CRL()
+ with pytest.raises(ValueError):
+ crl.export(self.cert, self.pkey, 100, 10, digest=b"sha256")
+
+ def test_export_unknown_digest(self):
+ """
+ Calling `OpenSSL.CRL.export` with an unsupported digest results
+ in a `ValueError` being raised.
+ """
+ crl = CRL()
+ with pytest.raises(ValueError):
+ crl.export(
+ self.cert, self.pkey, FILETYPE_PEM, 10, b"strange-digest")
+
+ def test_get_revoked(self):
+ """
+ Use python to create a simple CRL with two revocations. Get back the
+ `Revoked` using `OpenSSL.CRL.get_revoked` and verify them.
+ """
+ crl = CRL()
+
+ revoked = Revoked()
+ now = datetime.now().strftime("%Y%m%d%H%M%SZ").encode("ascii")
+ revoked.set_rev_date(now)
+ revoked.set_serial(b'3ab')
+ crl.add_revoked(revoked)
+ revoked.set_serial(b'100')
+ revoked.set_reason(b'sUpErSeDEd')
+ crl.add_revoked(revoked)
+
+ revs = crl.get_revoked()
+ assert len(revs) == 2
+ assert type(revs[0]) == Revoked
+ assert type(revs[1]) == Revoked
+ assert revs[0].get_serial() == b'03AB'
+ assert revs[1].get_serial() == b'0100'
+ assert revs[0].get_rev_date() == now
+ assert revs[1].get_rev_date() == now
+
+ def test_load_crl(self):
+ """
+ Load a known CRL and inspect its revocations. Both EM and DER formats
+ are loaded.
+ """
+ crl = load_crl(FILETYPE_PEM, crlData)
+ revs = crl.get_revoked()
+ assert len(revs) == 2
+ assert revs[0].get_serial() == b'03AB'
+ assert revs[0].get_reason() is None
+ assert revs[1].get_serial() == b'0100'
+ assert revs[1].get_reason() == b'Superseded'
+
+ der = _runopenssl(crlData, b"crl", b"-outform", b"DER")
+ crl = load_crl(FILETYPE_ASN1, der)
+ revs = crl.get_revoked()
+ assert len(revs) == 2
+ assert revs[0].get_serial() == b'03AB'
+ assert revs[0].get_reason() is None
+ assert revs[1].get_serial() == b'0100'
+ assert revs[1].get_reason() == b'Superseded'
+
+ def test_load_crl_bad_filetype(self):
+ """
+ Calling `OpenSSL.crypto.load_crl` with an unknown file type raises a
+ `ValueError`.
+ """
+ with pytest.raises(ValueError):
+ load_crl(100, crlData)
+
+ def test_load_crl_bad_data(self):
+ """
+ Calling `OpenSSL.crypto.load_crl` with file data which can't be loaded
+ raises a `OpenSSL.crypto.Error`.
+ """
+ with pytest.raises(Error):
+ load_crl(FILETYPE_PEM, b"hello, world")
+
+ def test_get_issuer(self):
+ """
+ Load a known CRL and assert its issuer's common name is what we expect
+ from the encoded crlData string.
+ """
+ crl = load_crl(FILETYPE_PEM, crlData)
+ assert isinstance(crl.get_issuer(), X509Name)
+ assert crl.get_issuer().CN == 'Testing Root CA'
+
+ def test_dump_crl(self):
+ """
+ The dumped CRL matches the original input.
+ """
+ crl = load_crl(FILETYPE_PEM, crlData)
+ buf = dump_crl(FILETYPE_PEM, crl)
+ assert buf == crlData
+
+ def _make_test_crl(self, issuer_cert, issuer_key, certs=()):
+ """
+ Create a CRL.
+
+ :param list[X509] certs: A list of certificates to revoke.
+ :rtype: CRL
+ """
+ crl = CRL()
+ for cert in certs:
+ revoked = Revoked()
+ # FIXME: This string splicing is an unfortunate implementation
+ # detail that has been reported in
+ # https://github.com/pyca/pyopenssl/issues/258
+ serial = hex(cert.get_serial_number())[2:].encode('utf-8')
+ revoked.set_serial(serial)
+ revoked.set_reason(b'unspecified')
+ revoked.set_rev_date(b'20140601000000Z')
+ crl.add_revoked(revoked)
+ crl.set_version(1)
+ crl.set_lastUpdate(b'20140601000000Z')
+ crl.set_nextUpdate(b'20180601000000Z')
+ crl.sign(issuer_cert, issuer_key, digest=b'sha512')
+ return crl
+
+ def test_verify_with_revoked(self):
+ """
+ `verify_certificate` raises error when an intermediate certificate is
+ revoked.
+ """
+ store = X509Store()
+ store.add_cert(self.root_cert)
+ store.add_cert(self.intermediate_cert)
+ root_crl = self._make_test_crl(
+ self.root_cert, self.root_key, certs=[self.intermediate_cert])
+ intermediate_crl = self._make_test_crl(
+ self.intermediate_cert, self.intermediate_key, certs=[])
+ store.add_crl(root_crl)
+ store.add_crl(intermediate_crl)
+ store.set_flags(
+ X509StoreFlags.CRL_CHECK | X509StoreFlags.CRL_CHECK_ALL)
+ store_ctx = X509StoreContext(store, self.intermediate_server_cert)
+ with pytest.raises(X509StoreContextError) as err:
+ store_ctx.verify_certificate()
+ assert err.value.args[0][2] == 'certificate revoked'
+
+ def test_verify_with_missing_crl(self):
+ """
+ `verify_certificate` raises error when an intermediate certificate's
+ CRL is missing.
+ """
+ store = X509Store()
+ store.add_cert(self.root_cert)
+ store.add_cert(self.intermediate_cert)
+ root_crl = self._make_test_crl(
+ self.root_cert, self.root_key, certs=[self.intermediate_cert])
+ store.add_crl(root_crl)
+ store.set_flags(
+ X509StoreFlags.CRL_CHECK | X509StoreFlags.CRL_CHECK_ALL)
+ store_ctx = X509StoreContext(store, self.intermediate_server_cert)
+ with pytest.raises(X509StoreContextError) as err:
+ store_ctx.verify_certificate()
+ assert err.value.args[0][2] == 'unable to get certificate CRL'
+ assert err.value.certificate.get_subject().CN == 'intermediate-service'
+
+ def test_convert_from_cryptography(self):
+ crypto_crl = x509.load_pem_x509_crl(crlData, backend)
+ crl = CRL.from_cryptography(crypto_crl)
+ assert isinstance(crl, CRL)
+
+ def test_convert_from_cryptography_unsupported_type(self):
+ with pytest.raises(TypeError):
+ CRL.from_cryptography(object())
+
+ def test_convert_to_cryptography_key(self):
+ crl = load_crl(FILETYPE_PEM, crlData)
+ crypto_crl = crl.to_cryptography()
+ assert isinstance(crypto_crl, x509.CertificateRevocationList)
+
+
+class TestX509StoreContext(object):
+ """
+ Tests for `OpenSSL.crypto.X509StoreContext`.
+ """
+ root_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ intermediate_cert = load_certificate(FILETYPE_PEM, intermediate_cert_pem)
+ intermediate_server_cert = load_certificate(
+ FILETYPE_PEM, intermediate_server_cert_pem)
+
+ def test_valid(self):
+ """
+ `verify_certificate` returns ``None`` when called with a certificate
+ and valid chain.
+ """
+ store = X509Store()
+ store.add_cert(self.root_cert)
+ store.add_cert(self.intermediate_cert)
+ store_ctx = X509StoreContext(store, self.intermediate_server_cert)
+ assert store_ctx.verify_certificate() is None
+
+ def test_reuse(self):
+ """
+ `verify_certificate` can be called multiple times with the same
+ ``X509StoreContext`` instance to produce the same result.
+ """
+ store = X509Store()
+ store.add_cert(self.root_cert)
+ store.add_cert(self.intermediate_cert)
+ store_ctx = X509StoreContext(store, self.intermediate_server_cert)
+ assert store_ctx.verify_certificate() is None
+ assert store_ctx.verify_certificate() is None
+
+ def test_trusted_self_signed(self):
+ """
+ `verify_certificate` returns ``None`` when called with a self-signed
+ certificate and itself in the chain.
+ """
+ store = X509Store()
+ store.add_cert(self.root_cert)
+ store_ctx = X509StoreContext(store, self.root_cert)
+ assert store_ctx.verify_certificate() is None
+
+ def test_untrusted_self_signed(self):
+ """
+ `verify_certificate` raises error when a self-signed certificate is
+ verified without itself in the chain.
+ """
+ store = X509Store()
+ store_ctx = X509StoreContext(store, self.root_cert)
+ with pytest.raises(X509StoreContextError) as exc:
+ store_ctx.verify_certificate()
+
+ assert exc.value.args[0][2] == 'self signed certificate'
+ assert exc.value.certificate.get_subject().CN == 'Testing Root CA'
+
+ def test_invalid_chain_no_root(self):
+ """
+ `verify_certificate` raises error when a root certificate is missing
+ from the chain.
+ """
+ store = X509Store()
+ store.add_cert(self.intermediate_cert)
+ store_ctx = X509StoreContext(store, self.intermediate_server_cert)
+
+ with pytest.raises(X509StoreContextError) as exc:
+ store_ctx.verify_certificate()
+
+ assert exc.value.args[0][2] == 'unable to get issuer certificate'
+ assert exc.value.certificate.get_subject().CN == 'intermediate'
+
+ def test_invalid_chain_no_intermediate(self):
+ """
+ `verify_certificate` raises error when an intermediate certificate is
+ missing from the chain.
+ """
+ store = X509Store()
+ store.add_cert(self.root_cert)
+ store_ctx = X509StoreContext(store, self.intermediate_server_cert)
+
+ with pytest.raises(X509StoreContextError) as exc:
+ store_ctx.verify_certificate()
+
+ assert exc.value.args[0][2] == 'unable to get local issuer certificate'
+ assert exc.value.certificate.get_subject().CN == 'intermediate-service'
+
+ def test_modification_pre_verify(self):
+ """
+ `verify_certificate` can use a store context modified after
+ instantiation.
+ """
+ store_bad = X509Store()
+ store_bad.add_cert(self.intermediate_cert)
+ store_good = X509Store()
+ store_good.add_cert(self.root_cert)
+ store_good.add_cert(self.intermediate_cert)
+ store_ctx = X509StoreContext(store_bad, self.intermediate_server_cert)
+
+ with pytest.raises(X509StoreContextError) as exc:
+ store_ctx.verify_certificate()
+
+ assert exc.value.args[0][2] == 'unable to get issuer certificate'
+ assert exc.value.certificate.get_subject().CN == 'intermediate'
+
+ store_ctx.set_store(store_good)
+ assert store_ctx.verify_certificate() is None
+
+ def test_verify_with_time(self):
+ """
+ `verify_certificate` raises error when the verification time is
+ set at notAfter.
+ """
+ store = X509Store()
+ store.add_cert(self.root_cert)
+ store.add_cert(self.intermediate_cert)
+
+ expire_time = self.intermediate_server_cert.get_notAfter()
+ expire_datetime = datetime.strptime(
+ expire_time.decode('utf-8'), '%Y%m%d%H%M%SZ'
+ )
+ store.set_time(expire_datetime)
+
+ store_ctx = X509StoreContext(store, self.intermediate_server_cert)
+ with pytest.raises(X509StoreContextError) as exc:
+ store_ctx.verify_certificate()
+
+ assert exc.value.args[0][2] == 'certificate has expired'
+
+
+class TestSignVerify(object):
+ """
+ Tests for `OpenSSL.crypto.sign` and `OpenSSL.crypto.verify`.
+ """
+
+ def test_sign_verify(self):
+ """
+ `sign` generates a cryptographic signature which `verify` can check.
+ """
+ content = (
+ b"It was a bright cold day in April, and the clocks were striking "
+ b"thirteen. Winston Smith, his chin nuzzled into his breast in an "
+ b"effort to escape the vile wind, slipped quickly through the "
+ b"glass doors of Victory Mansions, though not quickly enough to "
+ b"prevent a swirl of gritty dust from entering along with him.")
+
+ # sign the content with this private key
+ priv_key = load_privatekey(FILETYPE_PEM, root_key_pem)
+ # verify the content with this cert
+ good_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ # certificate unrelated to priv_key, used to trigger an error
+ bad_cert = load_certificate(FILETYPE_PEM, server_cert_pem)
+
+ for digest in ['md5', 'sha1']:
+ sig = sign(priv_key, content, digest)
+
+ # Verify the signature of content, will throw an exception if
+ # error.
+ verify(good_cert, sig, content, digest)
+
+ # This should fail because the certificate doesn't match the
+ # private key that was used to sign the content.
+ with pytest.raises(Error):
+ verify(bad_cert, sig, content, digest)
+
+ # This should fail because we've "tainted" the content after
+ # signing it.
+ with pytest.raises(Error):
+ verify(good_cert, sig, content + b"tainted", digest)
+
+ # test that unknown digest types fail
+ with pytest.raises(ValueError):
+ sign(priv_key, content, "strange-digest")
+ with pytest.raises(ValueError):
+ verify(good_cert, sig, content, "strange-digest")
+
+ def test_sign_verify_with_text(self):
+ """
+ `sign` generates a cryptographic signature which
+ `verify` can check. Deprecation warnings raised because using
+ text instead of bytes as content
+ """
+ content = (
+ b"It was a bright cold day in April, and the clocks were striking "
+ b"thirteen. Winston Smith, his chin nuzzled into his breast in an "
+ b"effort to escape the vile wind, slipped quickly through the "
+ b"glass doors of Victory Mansions, though not quickly enough to "
+ b"prevent a swirl of gritty dust from entering along with him."
+ ).decode("ascii")
+
+ priv_key = load_privatekey(FILETYPE_PEM, root_key_pem)
+ cert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ for digest in ['md5', 'sha1']:
+ with pytest.warns(DeprecationWarning) as w:
+ simplefilter("always")
+ sig = sign(priv_key, content, digest)
+ assert (
+ "{0} for data is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ ) == str(w[-1].message))
+
+ with pytest.warns(DeprecationWarning) as w:
+ simplefilter("always")
+ verify(cert, sig, content, digest)
+ assert (
+ "{0} for data is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ ) == str(w[-1].message))
+
+ def test_sign_verify_ecdsa(self):
+ """
+ `sign` generates a cryptographic signature which `verify` can check.
+ ECDSA Signatures in the X9.62 format may have variable length,
+ different from the length of the private key.
+ """
+ content = (
+ b"It was a bright cold day in April, and the clocks were striking "
+ b"thirteen. Winston Smith, his chin nuzzled into his breast in an "
+ b"effort to escape the vile wind, slipped quickly through the "
+ b"glass doors of Victory Mansions, though not quickly enough to "
+ b"prevent a swirl of gritty dust from entering along with him."
+ ).decode("ascii")
+ priv_key = load_privatekey(FILETYPE_PEM, ec_root_key_pem)
+ cert = load_certificate(FILETYPE_PEM, ec_root_cert_pem)
+ sig = sign(priv_key, content, "sha1")
+ verify(cert, sig, content, "sha1")
+
+ def test_sign_nulls(self):
+ """
+ `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)
+ good_cert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ sig = sign(priv_key, content, "sha1")
+ verify(good_cert, sig, content, "sha1")
+
+ def test_sign_with_large_key(self):
+ """
+ `sign` produces a signature for a string when using a long key.
+ """
+ content = (
+ b"It was a bright cold day in April, and the clocks were striking "
+ b"thirteen. Winston Smith, his chin nuzzled into his breast in an "
+ b"effort to escape the vile wind, slipped quickly through the "
+ b"glass doors of Victory Mansions, though not quickly enough to "
+ b"prevent a swirl of gritty dust from entering along with him.")
+
+ priv_key = load_privatekey(FILETYPE_PEM, large_key_pem)
+ sign(priv_key, content, "sha1")
+
+
+class TestEllipticCurve(object):
+ """
+ Tests for `_EllipticCurve`, `get_elliptic_curve`, and
+ `get_elliptic_curves`.
+ """
+
+ def test_set(self):
+ """
+ `get_elliptic_curves` returns a `set`.
+ """
+ assert isinstance(get_elliptic_curves(), set)
+
+ def test_a_curve(self):
+ """
+ `get_elliptic_curve` can be used to retrieve a particular supported
+ curve.
+ """
+ curves = get_elliptic_curves()
+ curve = next(iter(curves))
+ assert curve.name == get_elliptic_curve(curve.name).name
+
+ def test_not_a_curve(self):
+ """
+ `get_elliptic_curve` raises `ValueError` if called with a name which
+ does not identify a supported curve.
+ """
+ with pytest.raises(ValueError):
+ get_elliptic_curve(u"this curve was just invented")
+
+ def test_repr(self):
+ """
+ The string representation of a curve object includes simply states the
+ object is a curve and what its name is.
+ """
+ curves = get_elliptic_curves()
+ curve = next(iter(curves))
+ assert "<Curve %r>" % (curve.name,) == repr(curve)
+
+ def test_to_EC_KEY(self):
+ """
+ The curve object can export a version of itself as an EC_KEY* via the
+ private `_EllipticCurve._to_EC_KEY`.
+ """
+ curves = get_elliptic_curves()
+ curve = next(iter(curves))
+ # It's not easy to assert anything about this object. However, see
+ # leakcheck/crypto.py for a test that demonstrates it at least does
+ # not leak memory.
+ curve._to_EC_KEY()
+
+
+class EllipticCurveFactory(object):
+ """
+ A helper to get the names of two curves.
+ """
+
+ def __init__(self):
+ curves = iter(get_elliptic_curves())
+ self.curve_name = next(curves).name
+ self.another_curve_name = next(curves).name
+
+
+class TestEllipticCurveEquality(EqualityTestsMixin):
+ """
+ Tests `_EllipticCurve`'s implementation of ``==`` and ``!=``.
+ """
+ curve_factory = EllipticCurveFactory()
+
+ if curve_factory.curve_name is None:
+ skip = "There are no curves available there can be no curve objects."
+
+ def anInstance(self):
+ """
+ Get the curve object for an arbitrary curve supported by the system.
+ """
+ return get_elliptic_curve(self.curve_factory.curve_name)
+
+ def anotherInstance(self):
+ """
+ Get the curve object for an arbitrary curve supported by the system -
+ but not the one returned by C{anInstance}.
+ """
+ return get_elliptic_curve(self.curve_factory.another_curve_name)
+
+
+class TestEllipticCurveHash(object):
+ """
+ Tests for `_EllipticCurve`'s implementation of hashing (thus use as
+ an item in a `dict` or `set`).
+ """
+ curve_factory = EllipticCurveFactory()
+
+ if curve_factory.curve_name is None:
+ skip = "There are no curves available there can be no curve objects."
+
+ def test_contains(self):
+ """
+ The ``in`` operator reports that a `set` containing a curve does
+ contain that curve.
+ """
+ curve = get_elliptic_curve(self.curve_factory.curve_name)
+ curves = set([curve])
+ assert curve in curves
+
+ def test_does_not_contain(self):
+ """
+ The ``in`` operator reports that a `set` not containing a curve
+ does not contain that curve.
+ """
+ curve = get_elliptic_curve(self.curve_factory.curve_name)
+ curves = set([
+ get_elliptic_curve(self.curve_factory.another_curve_name)
+ ])
+ assert curve not in curves
--- /dev/null
+from OpenSSL.debug import _env_info
+from OpenSSL import version
+
+
+def test_debug_info():
+ """
+ Debug info contains correct data.
+ """
+ # Just check a sample we control.
+ assert version.__version__ in _env_info
--- /dev/null
+# Copyright (c) Frederick Dean
+# See LICENSE for details.
+
+"""
+Unit tests for `OpenSSL.rand`.
+"""
+
+import pytest
+
+from OpenSSL import rand
+
+
+class TestRand(object):
+
+ @pytest.mark.parametrize('args', [
+ (b"foo", None),
+ (None, 3),
+ ])
+ def test_add_wrong_args(self, args):
+ """
+ `OpenSSL.rand.add` raises `TypeError` if called with arguments not of
+ type `str` and `int`.
+ """
+ with pytest.raises(TypeError):
+ rand.add(*args)
+
+ def test_add(self):
+ """
+ `OpenSSL.rand.add` adds entropy to the PRNG.
+ """
+ rand.add(b'hamburger', 3)
+
+ def test_status(self):
+ """
+ `OpenSSL.rand.status` returns `1` if the PRNG has sufficient entropy,
+ `0` otherwise.
+ """
+ assert rand.status() == 1
--- /dev/null
+# Copyright (C) Jean-Paul Calderone
+# See LICENSE for details.
+
+"""
+Unit tests for :mod:`OpenSSL.SSL`.
+"""
+
+import datetime
+import sys
+import uuid
+
+from gc import collect, get_referrers
+from errno import ECONNREFUSED, EINPROGRESS, EWOULDBLOCK, EPIPE, ESHUTDOWN
+from sys import platform, getfilesystemencoding
+from socket import MSG_PEEK, SHUT_RDWR, error, socket
+from os import makedirs
+from os.path import join
+from weakref import ref
+from warnings import simplefilter
+
+import pytest
+
+from pretend import raiser
+
+from six import PY3, text_type
+
+from cryptography import x509
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import rsa
+from cryptography.x509.oid import NameOID
+
+
+from OpenSSL.crypto import TYPE_RSA, FILETYPE_PEM
+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.crypto import get_elliptic_curves
+
+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,
+ 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 import SSL
+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 (
+ Context, ContextType, Session, Connection, ConnectionType, SSLeay_version)
+from OpenSSL.SSL import _make_requires
+
+from OpenSSL._util import ffi as _ffi, lib as _lib
+
+from OpenSSL.SSL import (
+ OP_NO_QUERY_MTU, OP_COOKIE_EXCHANGE, OP_NO_TICKET, OP_NO_COMPRESSION,
+ MODE_RELEASE_BUFFERS)
+
+from OpenSSL.SSL import (
+ SSL_ST_CONNECT, SSL_ST_ACCEPT, SSL_ST_MASK,
+ SSL_CB_LOOP, SSL_CB_EXIT, SSL_CB_READ, SSL_CB_WRITE, SSL_CB_ALERT,
+ SSL_CB_READ_ALERT, SSL_CB_WRITE_ALERT, SSL_CB_ACCEPT_LOOP,
+ SSL_CB_ACCEPT_EXIT, SSL_CB_CONNECT_LOOP, SSL_CB_CONNECT_EXIT,
+ SSL_CB_HANDSHAKE_START, SSL_CB_HANDSHAKE_DONE)
+
+try:
+ from OpenSSL.SSL import (
+ SSL_ST_INIT, SSL_ST_BEFORE, SSL_ST_OK, SSL_ST_RENEGOTIATE
+ )
+except ImportError:
+ SSL_ST_INIT = SSL_ST_BEFORE = SSL_ST_OK = SSL_ST_RENEGOTIATE = None
+
+from .util import WARNING_TYPE_EXPECTED, NON_ASCII, is_consistent_type
+from .test_crypto import (
+ cleartextCertificatePEM, cleartextPrivateKeyPEM,
+ client_cert_pem, client_key_pem, server_cert_pem, server_key_pem,
+ root_cert_pem)
+
+
+# openssl dhparam 1024 -out dh-1024.pem (note that 1024 is a small number of
+# bits to use)
+dhparam = """\
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBALdUMvn+C9MM+y5BWZs11mSeH6HHoEq0UVbzVq7UojC1hbsZUuGukQ3a
+Qh2/pwqb18BZFykrWB0zv/OkLa0kx4cuUgNrUVq1EFheBiX6YqryJ7t2sO09NQiO
+V7H54LmltOT/hEh6QWsJqb6BQgH65bswvV/XkYGja8/T0GzvbaVzAgEC
+-----END DH PARAMETERS-----
+"""
+
+
+skip_if_py3 = pytest.mark.skipif(PY3, reason="Python 2 only")
+
+
+def join_bytes_or_unicode(prefix, suffix):
+ """
+ Join two path components of either ``bytes`` or ``unicode``.
+
+ The return type is the same as the type of ``prefix``.
+ """
+ # If the types are the same, nothing special is necessary.
+ if type(prefix) == type(suffix):
+ return join(prefix, suffix)
+
+ # Otherwise, coerce suffix to the type of prefix.
+ if isinstance(prefix, text_type):
+ return join(prefix, suffix.decode(getfilesystemencoding()))
+ else:
+ return join(prefix, suffix.encode(getfilesystemencoding()))
+
+
+def verify_cb(conn, cert, errnum, depth, ok):
+ return ok
+
+
+def socket_pair():
+ """
+ Establish and return a pair of network sockets connected to each other.
+ """
+ # Connect a pair of sockets
+ port = socket()
+ port.bind(('', 0))
+ port.listen(1)
+ client = socket()
+ client.setblocking(False)
+ client.connect_ex(("127.0.0.1", port.getsockname()[1]))
+ client.setblocking(True)
+ server = port.accept()[0]
+
+ # Let's pass some unencrypted data to make sure our socket connection is
+ # fine. Just one byte, so we don't have to worry about buffers getting
+ # filled up or fragmentation.
+ server.send(b"x")
+ assert client.recv(1024) == b"x"
+ client.send(b"y")
+ assert server.recv(1024) == b"y"
+
+ # Most of our callers want non-blocking sockets, make it easy for them.
+ server.setblocking(False)
+ client.setblocking(False)
+
+ return (server, client)
+
+
+def handshake(client, server):
+ conns = [client, server]
+ while conns:
+ for conn in conns:
+ try:
+ conn.do_handshake()
+ except WantReadError:
+ pass
+ else:
+ conns.remove(conn)
+
+
+def _create_certificate_chain():
+ """
+ Construct and return a chain of certificates.
+
+ 1. A new self-signed certificate authority certificate (cacert)
+ 2. A new intermediate certificate signed by cacert (icert)
+ 3. A new server certificate signed by icert (scert)
+ """
+ caext = X509Extension(b'basicConstraints', False, b'CA:true')
+
+ # Step 1
+ cakey = PKey()
+ cakey.generate_key(TYPE_RSA, 1024)
+ cacert = X509()
+ cacert.get_subject().commonName = "Authority Certificate"
+ cacert.set_issuer(cacert.get_subject())
+ cacert.set_pubkey(cakey)
+ cacert.set_notBefore(b"20000101000000Z")
+ cacert.set_notAfter(b"29990101000000Z")
+ cacert.add_extensions([caext])
+ cacert.set_serial_number(0)
+ cacert.sign(cakey, "sha1")
+
+ # Step 2
+ ikey = PKey()
+ ikey.generate_key(TYPE_RSA, 1024)
+ icert = X509()
+ icert.get_subject().commonName = "Intermediate Certificate"
+ icert.set_issuer(cacert.get_subject())
+ icert.set_pubkey(ikey)
+ icert.set_notBefore(b"20000101000000Z")
+ icert.set_notAfter(b"29990101000000Z")
+ icert.add_extensions([caext])
+ icert.set_serial_number(0)
+ icert.sign(cakey, "sha1")
+
+ # Step 3
+ skey = PKey()
+ skey.generate_key(TYPE_RSA, 1024)
+ scert = X509()
+ scert.get_subject().commonName = "Server Certificate"
+ scert.set_issuer(icert.get_subject())
+ scert.set_pubkey(skey)
+ scert.set_notBefore(b"20000101000000Z")
+ scert.set_notAfter(b"29990101000000Z")
+ scert.add_extensions([
+ X509Extension(b'basicConstraints', True, b'CA:false')])
+ scert.set_serial_number(0)
+ scert.sign(ikey, "sha1")
+
+ return [(cakey, cacert), (ikey, icert), (skey, scert)]
+
+
+def loopback_client_factory(socket, version=SSLv23_METHOD):
+ client = Connection(Context(version), socket)
+ client.set_connect_state()
+ return client
+
+
+def loopback_server_factory(socket, version=SSLv23_METHOD):
+ ctx = Context(version)
+ ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
+ ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
+ server = Connection(ctx, socket)
+ server.set_accept_state()
+ return server
+
+
+def loopback(server_factory=None, client_factory=None):
+ """
+ Create a connected socket pair and force two connected SSL sockets
+ to talk to each other via memory BIOs.
+ """
+ if server_factory is None:
+ server_factory = loopback_server_factory
+ if client_factory is None:
+ client_factory = loopback_client_factory
+
+ (server, client) = socket_pair()
+ server = server_factory(server)
+ client = client_factory(client)
+
+ handshake(client, server)
+
+ server.setblocking(True)
+ client.setblocking(True)
+ return server, client
+
+
+def interact_in_memory(client_conn, server_conn):
+ """
+ Try to read application bytes from each of the two `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 `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.
+ """
+ wrote = True
+ while wrote:
+ # Loop until neither side has anything to say
+ wrote = False
+
+ # Copy stuff from each side's send buffer to the other side's
+ # receive buffer.
+ for (read, write) in [(client_conn, server_conn),
+ (server_conn, client_conn)]:
+
+ # Give the side a chance to generate some more bytes, or succeed.
+ try:
+ data = read.recv(2 ** 16)
+ except WantReadError:
+ # It didn't succeed, so we'll hope it generated some output.
+ pass
+ else:
+ # It did succeed, so we'll stop now and let the caller deal
+ # with it.
+ return (read, data)
+
+ while True:
+ # Keep copying as long as there's more stuff there.
+ try:
+ dirty = read.bio_read(4096)
+ except WantReadError:
+ # Okay, nothing more waiting to be sent. Stop
+ # processing this send buffer.
+ break
+ else:
+ # Keep track of the fact that someone generated some
+ # output.
+ wrote = True
+ write.bio_write(dirty)
+
+
+def handshake_in_memory(client_conn, server_conn):
+ """
+ Perform the TLS handshake between two `Connection` instances connected to
+ each other via memory BIOs.
+ """
+ client_conn.set_connect_state()
+ server_conn.set_accept_state()
+
+ for conn in [client_conn, server_conn]:
+ try:
+ conn.do_handshake()
+ except WantReadError:
+ pass
+
+ interact_in_memory(client_conn, server_conn)
+
+
+class TestVersion(object):
+ """
+ Tests for version information exposed by `OpenSSL.SSL.SSLeay_version` and
+ `OpenSSL.SSL.OPENSSL_VERSION_NUMBER`.
+ """
+ def test_OPENSSL_VERSION_NUMBER(self):
+ """
+ `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.
+ """
+ assert isinstance(OPENSSL_VERSION_NUMBER, int)
+
+ def test_SSLeay_version(self):
+ """
+ `SSLeay_version` takes a version type indicator and returns one of a
+ number of version strings based on that indicator.
+ """
+ versions = {}
+ for t in [SSLEAY_VERSION, SSLEAY_CFLAGS, SSLEAY_BUILT_ON,
+ SSLEAY_PLATFORM, SSLEAY_DIR]:
+ version = SSLeay_version(t)
+ versions[version] = t
+ assert isinstance(version, bytes)
+ assert len(versions) == 5
+
+
+@pytest.fixture
+def ca_file(tmpdir):
+ """
+ Create a valid PEM file with CA certificates and return the path.
+ """
+ key = rsa.generate_private_key(
+ public_exponent=65537,
+ key_size=2048,
+ backend=default_backend()
+ )
+ public_key = key.public_key()
+
+ builder = x509.CertificateBuilder()
+ builder = builder.subject_name(x509.Name([
+ x509.NameAttribute(NameOID.COMMON_NAME, u"pyopenssl.org"),
+ ]))
+ builder = builder.issuer_name(x509.Name([
+ x509.NameAttribute(NameOID.COMMON_NAME, u"pyopenssl.org"),
+ ]))
+ one_day = datetime.timedelta(1, 0, 0)
+ builder = builder.not_valid_before(datetime.datetime.today() - one_day)
+ builder = builder.not_valid_after(datetime.datetime.today() + one_day)
+ builder = builder.serial_number(int(uuid.uuid4()))
+ builder = builder.public_key(public_key)
+ builder = builder.add_extension(
+ x509.BasicConstraints(ca=True, path_length=None), critical=True,
+ )
+
+ certificate = builder.sign(
+ private_key=key, algorithm=hashes.SHA256(),
+ backend=default_backend()
+ )
+
+ ca_file = tmpdir.join("test.pem")
+ ca_file.write_binary(
+ certificate.public_bytes(
+ encoding=serialization.Encoding.PEM,
+ )
+ )
+
+ return str(ca_file).encode("ascii")
+
+
+@pytest.fixture
+def context():
+ """
+ A simple TLS 1.0 context.
+ """
+ return Context(TLSv1_METHOD)
+
+
+class TestContext(object):
+ """
+ Unit tests for `OpenSSL.SSL.Context`.
+ """
+ @pytest.mark.parametrize("cipher_string", [
+ b"hello world:AES128-SHA",
+ u"hello world:AES128-SHA",
+ ])
+ def test_set_cipher_list(self, context, cipher_string):
+ """
+ `Context.set_cipher_list` accepts both byte and unicode strings
+ for naming the ciphers which connections created with the context
+ object will be able to choose from.
+ """
+ context.set_cipher_list(cipher_string)
+ conn = Connection(context, None)
+
+ assert "AES128-SHA" in conn.get_cipher_list()
+
+ @pytest.mark.parametrize("cipher_list,error", [
+ (object(), TypeError),
+ ("imaginary-cipher", Error),
+ ])
+ def test_set_cipher_list_wrong_args(self, context, cipher_list, error):
+ """
+ `Context.set_cipher_list` raises `TypeError` when passed a non-string
+ argument and raises `OpenSSL.SSL.Error` when passed an incorrect cipher
+ list string.
+ """
+ with pytest.raises(error):
+ context.set_cipher_list(cipher_list)
+
+ def test_load_client_ca(self, context, ca_file):
+ """
+ `Context.load_client_ca` works as far as we can tell.
+ """
+ context.load_client_ca(ca_file)
+
+ def test_load_client_ca_invalid(self, context, tmpdir):
+ """
+ `Context.load_client_ca` raises an Error if the ca file is invalid.
+ """
+ ca_file = tmpdir.join("test.pem")
+ ca_file.write("")
+
+ with pytest.raises(Error) as e:
+ context.load_client_ca(str(ca_file).encode("ascii"))
+
+ assert "PEM routines" == e.value.args[0][0][0]
+
+ def test_load_client_ca_unicode(self, context, ca_file):
+ """
+ Passing the path as unicode raises a warning but works.
+ """
+ pytest.deprecated_call(
+ context.load_client_ca, ca_file.decode("ascii")
+ )
+
+ def test_set_session_id(self, context):
+ """
+ `Context.set_session_id` works as far as we can tell.
+ """
+ context.set_session_id(b"abc")
+
+ def test_set_session_id_fail(self, context):
+ """
+ `Context.set_session_id` errors are propagated.
+ """
+ with pytest.raises(Error) as e:
+ context.set_session_id(b"abc" * 1000)
+
+ assert [
+ ("SSL routines",
+ "SSL_CTX_set_session_id_context",
+ "ssl session id context too long")
+ ] == e.value.args[0]
+
+ def test_set_session_id_unicode(self, context):
+ """
+ `Context.set_session_id` raises a warning if a unicode string is
+ passed.
+ """
+ pytest.deprecated_call(context.set_session_id, u"abc")
+
+ def test_method(self):
+ """
+ `Context` can be instantiated with one of `SSLv2_METHOD`,
+ `SSLv3_METHOD`, `SSLv23_METHOD`, `TLSv1_METHOD`, `TLSv1_1_METHOD`,
+ or `TLSv1_2_METHOD`.
+ """
+ methods = [SSLv23_METHOD, TLSv1_METHOD]
+ for meth in methods:
+ Context(meth)
+
+ maybe = [SSLv2_METHOD, SSLv3_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
+
+ with pytest.raises(TypeError):
+ Context("")
+ with pytest.raises(ValueError):
+ Context(10)
+
+ @skip_if_py3
+ def test_method_long(self):
+ """
+ On Python 2 `Context` accepts values of type `long` as well as `int`.
+ """
+ Context(long(TLSv1_METHOD))
+
+ def test_type(self):
+ """
+ `Context` and `ContextType` refer to the same type object and can
+ be used to create instances of that type.
+ """
+ assert Context is ContextType
+ assert is_consistent_type(Context, 'Context', TLSv1_METHOD)
+
+ def test_use_privatekey(self):
+ """
+ `Context.use_privatekey` takes an `OpenSSL.crypto.PKey` instance.
+ """
+ key = PKey()
+ key.generate_key(TYPE_RSA, 512)
+ ctx = Context(TLSv1_METHOD)
+ ctx.use_privatekey(key)
+ with pytest.raises(TypeError):
+ ctx.use_privatekey("")
+
+ def test_use_privatekey_file_missing(self, tmpfile):
+ """
+ `Context.use_privatekey_file` raises `OpenSSL.SSL.Error` when passed
+ the name of a file which does not exist.
+ """
+ ctx = Context(TLSv1_METHOD)
+ with pytest.raises(Error):
+ ctx.use_privatekey_file(tmpfile)
+
+ def _use_privatekey_file_test(self, pemfile, filetype):
+ """
+ Verify that calling ``Context.use_privatekey_file`` with the given
+ arguments does not raise an exception.
+ """
+ key = PKey()
+ key.generate_key(TYPE_RSA, 512)
+
+ with open(pemfile, "wt") as pem:
+ pem.write(
+ dump_privatekey(FILETYPE_PEM, key).decode("ascii")
+ )
+
+ ctx = Context(TLSv1_METHOD)
+ ctx.use_privatekey_file(pemfile, filetype)
+
+ @pytest.mark.parametrize('filetype', [object(), "", None, 1.0])
+ def test_wrong_privatekey_file_wrong_args(self, tmpfile, filetype):
+ """
+ `Context.use_privatekey_file` raises `TypeError` when called with
+ a `filetype` which is not a valid file encoding.
+ """
+ ctx = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ ctx.use_privatekey_file(tmpfile, filetype)
+
+ def test_use_privatekey_file_bytes(self, tmpfile):
+ """
+ A private key can be specified from a file by passing a ``bytes``
+ instance giving the file name to ``Context.use_privatekey_file``.
+ """
+ self._use_privatekey_file_test(
+ tmpfile + NON_ASCII.encode(getfilesystemencoding()),
+ FILETYPE_PEM,
+ )
+
+ def test_use_privatekey_file_unicode(self, tmpfile):
+ """
+ A private key can be specified from a file by passing a ``unicode``
+ instance giving the file name to ``Context.use_privatekey_file``.
+ """
+ self._use_privatekey_file_test(
+ tmpfile.decode(getfilesystemencoding()) + NON_ASCII,
+ FILETYPE_PEM,
+ )
+
+ @skip_if_py3
+ def test_use_privatekey_file_long(self, tmpfile):
+ """
+ On Python 2 `Context.use_privatekey_file` accepts a filetype of
+ type `long` as well as `int`.
+ """
+ self._use_privatekey_file_test(tmpfile, long(FILETYPE_PEM))
+
+ def test_use_certificate_wrong_args(self):
+ """
+ `Context.use_certificate_wrong_args` raises `TypeError` when not passed
+ exactly one `OpenSSL.crypto.X509` instance as an argument.
+ """
+ ctx = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ ctx.use_certificate("hello, world")
+
+ def test_use_certificate_uninitialized(self):
+ """
+ `Context.use_certificate` raises `OpenSSL.SSL.Error` when passed a
+ `OpenSSL.crypto.X509` instance which has not been initialized
+ (ie, which does not actually have any certificate data).
+ """
+ ctx = Context(TLSv1_METHOD)
+ with pytest.raises(Error):
+ ctx.use_certificate(X509())
+
+ def test_use_certificate(self):
+ """
+ `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):
+ """
+ `Context.use_certificate_file` raises `TypeError` if the first
+ argument is not a byte string or the second argument is not an integer.
+ """
+ ctx = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ ctx.use_certificate_file(object(), FILETYPE_PEM)
+ with pytest.raises(TypeError):
+ ctx.use_certificate_file(b"somefile", object())
+ with pytest.raises(TypeError):
+ ctx.use_certificate_file(object(), FILETYPE_PEM)
+
+ def test_use_certificate_file_missing(self, tmpfile):
+ """
+ `Context.use_certificate_file` raises `OpenSSL.SSL.Error` if passed
+ the name of a file which does not exist.
+ """
+ ctx = Context(TLSv1_METHOD)
+ with pytest.raises(Error):
+ ctx.use_certificate_file(tmpfile)
+
+ def _use_certificate_file_test(self, certificate_file):
+ """
+ Verify that calling ``Context.use_certificate_file`` with the given
+ filename doesn't raise an exception.
+ """
+ # 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...
+ with open(certificate_file, "wb") as pem_file:
+ pem_file.write(cleartextCertificatePEM)
+
+ ctx = Context(TLSv1_METHOD)
+ ctx.use_certificate_file(certificate_file)
+
+ def test_use_certificate_file_bytes(self, tmpfile):
+ """
+ `Context.use_certificate_file` sets the certificate (given as a
+ `bytes` filename) which will be used to identify connections created
+ using the context.
+ """
+ filename = tmpfile + NON_ASCII.encode(getfilesystemencoding())
+ self._use_certificate_file_test(filename)
+
+ def test_use_certificate_file_unicode(self, tmpfile):
+ """
+ `Context.use_certificate_file` sets the certificate (given as a
+ `bytes` filename) which will be used to identify connections created
+ using the context.
+ """
+ filename = tmpfile.decode(getfilesystemencoding()) + NON_ASCII
+ self._use_certificate_file_test(filename)
+
+ @skip_if_py3
+ def test_use_certificate_file_long(self, tmpfile):
+ """
+ On Python 2 `Context.use_certificate_file` accepts a
+ filetype of type `long` as well as `int`.
+ """
+ pem_filename = tmpfile
+ 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_check_privatekey_valid(self):
+ """
+ `Context.check_privatekey` returns `None` if the `Context` instance
+ has been configured to use a matched key and certificate pair.
+ """
+ key = load_privatekey(FILETYPE_PEM, client_key_pem)
+ cert = load_certificate(FILETYPE_PEM, client_cert_pem)
+ context = Context(TLSv1_METHOD)
+ context.use_privatekey(key)
+ context.use_certificate(cert)
+ assert None is context.check_privatekey()
+
+ def test_check_privatekey_invalid(self):
+ """
+ `Context.check_privatekey` raises `Error` if the `Context` instance
+ has been configured to use a key and certificate pair which don't
+ relate to each other.
+ """
+ key = load_privatekey(FILETYPE_PEM, client_key_pem)
+ cert = load_certificate(FILETYPE_PEM, server_cert_pem)
+ context = Context(TLSv1_METHOD)
+ context.use_privatekey(key)
+ context.use_certificate(cert)
+ with pytest.raises(Error):
+ context.check_privatekey()
+
+ def test_app_data(self):
+ """
+ `Context.set_app_data` stores an object for later retrieval
+ using `Context.get_app_data`.
+ """
+ app_data = object()
+ context = Context(TLSv1_METHOD)
+ context.set_app_data(app_data)
+ assert context.get_app_data() is app_data
+
+ def test_set_options_wrong_args(self):
+ """
+ `Context.set_options` raises `TypeError` if called with
+ a non-`int` argument.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ context.set_options(None)
+
+ def test_set_options(self):
+ """
+ `Context.set_options` returns the new options value.
+ """
+ context = Context(TLSv1_METHOD)
+ options = context.set_options(OP_NO_SSLv2)
+ assert options & OP_NO_SSLv2 == OP_NO_SSLv2
+
+ @skip_if_py3
+ def test_set_options_long(self):
+ """
+ On Python 2 `Context.set_options` accepts values of type
+ `long` as well as `int`.
+ """
+ context = Context(TLSv1_METHOD)
+ options = context.set_options(long(OP_NO_SSLv2))
+ assert options & OP_NO_SSLv2 == OP_NO_SSLv2
+
+ def test_set_mode_wrong_args(self):
+ """
+ `Context.set_mode` raises `TypeError` if called with
+ a non-`int` argument.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ context.set_mode(None)
+
+ def test_set_mode(self):
+ """
+ `Context.set_mode` accepts a mode bitvector and returns the
+ newly set mode.
+ """
+ context = Context(TLSv1_METHOD)
+ assert MODE_RELEASE_BUFFERS & context.set_mode(MODE_RELEASE_BUFFERS)
+
+ @skip_if_py3
+ def test_set_mode_long(self):
+ """
+ On Python 2 `Context.set_mode` accepts values of type `long` as well
+ as `int`.
+ """
+ context = Context(TLSv1_METHOD)
+ mode = context.set_mode(long(MODE_RELEASE_BUFFERS))
+ assert MODE_RELEASE_BUFFERS & mode
+
+ def test_set_timeout_wrong_args(self):
+ """
+ `Context.set_timeout` raises `TypeError` if called with
+ a non-`int` argument.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ context.set_timeout(None)
+
+ def test_timeout(self):
+ """
+ `Context.set_timeout` sets the session timeout for all connections
+ created using the context object. `Context.get_timeout` retrieves
+ this value.
+ """
+ context = Context(TLSv1_METHOD)
+ context.set_timeout(1234)
+ assert context.get_timeout() == 1234
+
+ @skip_if_py3
+ def test_timeout_long(self):
+ """
+ On Python 2 `Context.set_timeout` accepts values of type `long` as
+ well as int.
+ """
+ context = Context(TLSv1_METHOD)
+ context.set_timeout(long(1234))
+ assert context.get_timeout() == 1234
+
+ def test_set_verify_depth_wrong_args(self):
+ """
+ `Context.set_verify_depth` raises `TypeError` if called with a
+ non-`int` argument.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ context.set_verify_depth(None)
+
+ def test_verify_depth(self):
+ """
+ `Context.set_verify_depth` sets the number of certificates in
+ a chain to follow before giving up. The value can be retrieved with
+ `Context.get_verify_depth`.
+ """
+ context = Context(TLSv1_METHOD)
+ context.set_verify_depth(11)
+ assert context.get_verify_depth() == 11
+
+ @skip_if_py3
+ def test_verify_depth_long(self):
+ """
+ On Python 2 `Context.set_verify_depth` accepts values of type `long`
+ as well as int.
+ """
+ context = Context(TLSv1_METHOD)
+ context.set_verify_depth(long(11))
+ assert context.get_verify_depth() == 11
+
+ def _write_encrypted_pem(self, passphrase, tmpfile):
+ """
+ Write a new private key out to a new file, encrypted using the given
+ passphrase. Return the path to the new file.
+ """
+ key = PKey()
+ key.generate_key(TYPE_RSA, 512)
+ pem = dump_privatekey(FILETYPE_PEM, key, "blowfish", passphrase)
+ with open(tmpfile, 'w') as fObj:
+ fObj.write(pem.decode('ascii'))
+ return tmpfile
+
+ def test_set_passwd_cb_wrong_args(self):
+ """
+ `Context.set_passwd_cb` raises `TypeError` if called with a
+ non-callable first argument.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ context.set_passwd_cb(None)
+
+ def test_set_passwd_cb(self, tmpfile):
+ """
+ `Context.set_passwd_cb` accepts a callable which will be invoked when
+ a private key is loaded from an encrypted PEM.
+ """
+ passphrase = b"foobar"
+ pemFile = self._write_encrypted_pem(passphrase, tmpfile)
+ calledWith = []
+
+ def passphraseCallback(maxlen, verify, extra):
+ calledWith.append((maxlen, verify, extra))
+ return passphrase
+ context = Context(TLSv1_METHOD)
+ context.set_passwd_cb(passphraseCallback)
+ context.use_privatekey_file(pemFile)
+ assert len(calledWith) == 1
+ assert isinstance(calledWith[0][0], int)
+ assert isinstance(calledWith[0][1], int)
+ assert calledWith[0][2] is None
+
+ def test_passwd_callback_exception(self, tmpfile):
+ """
+ `Context.use_privatekey_file` propagates any exception raised
+ by the passphrase callback.
+ """
+ pemFile = self._write_encrypted_pem(b"monkeys are nice", tmpfile)
+
+ def passphraseCallback(maxlen, verify, extra):
+ raise RuntimeError("Sorry, I am a fail.")
+
+ context = Context(TLSv1_METHOD)
+ context.set_passwd_cb(passphraseCallback)
+ with pytest.raises(RuntimeError):
+ context.use_privatekey_file(pemFile)
+
+ def test_passwd_callback_false(self, tmpfile):
+ """
+ `Context.use_privatekey_file` raises `OpenSSL.SSL.Error` if the
+ passphrase callback returns a false value.
+ """
+ pemFile = self._write_encrypted_pem(b"monkeys are nice", tmpfile)
+
+ def passphraseCallback(maxlen, verify, extra):
+ return b""
+
+ context = Context(TLSv1_METHOD)
+ context.set_passwd_cb(passphraseCallback)
+ with pytest.raises(Error):
+ context.use_privatekey_file(pemFile)
+
+ def test_passwd_callback_non_string(self, tmpfile):
+ """
+ `Context.use_privatekey_file` raises `OpenSSL.SSL.Error` if the
+ passphrase callback returns a true non-string value.
+ """
+ pemFile = self._write_encrypted_pem(b"monkeys are nice", tmpfile)
+
+ def passphraseCallback(maxlen, verify, extra):
+ return 10
+
+ context = Context(TLSv1_METHOD)
+ context.set_passwd_cb(passphraseCallback)
+ # TODO: Surely this is the wrong error?
+ with pytest.raises(ValueError):
+ context.use_privatekey_file(pemFile)
+
+ def test_passwd_callback_too_long(self, tmpfile):
+ """
+ If the passphrase returned by the passphrase callback returns a string
+ longer than the indicated maximum length, it is truncated.
+ """
+ # A priori knowledge!
+ passphrase = b"x" * 1024
+ pemFile = self._write_encrypted_pem(passphrase, tmpfile)
+
+ def passphraseCallback(maxlen, verify, extra):
+ assert maxlen == 1024
+ return passphrase + b"y"
+
+ context = Context(TLSv1_METHOD)
+ context.set_passwd_cb(passphraseCallback)
+ # This shall succeed because the truncated result is the correct
+ # passphrase.
+ context.use_privatekey_file(pemFile)
+
+ def test_set_info_callback(self):
+ """
+ `Context.set_info_callback` accepts a callable which will be
+ invoked when certain information about an SSL connection is available.
+ """
+ (server, client) = socket_pair()
+
+ clientSSL = Connection(Context(TLSv1_METHOD), client)
+ clientSSL.set_connect_state()
+
+ called = []
+
+ def info(conn, where, ret):
+ called.append((conn, where, ret))
+ context = Context(TLSv1_METHOD)
+ context.set_info_callback(info)
+ context.use_certificate(
+ load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
+ context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
+
+ serverSSL = Connection(context, server)
+ serverSSL.set_accept_state()
+
+ handshake(clientSSL, serverSSL)
+
+ # 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)]
+ assert [] == notConnections, (
+ "Some info callback arguments were not Connection instances.")
+
+ def _load_verify_locations_test(self, *args):
+ """
+ Create a client context which will verify the peer certificate and call
+ its `load_verify_locations` method with the given arguments.
+ Then connect it to a server and ensure that the handshake succeeds.
+ """
+ (server, client) = socket_pair()
+
+ clientContext = Context(TLSv1_METHOD)
+ clientContext.load_verify_locations(*args)
+ # Require that the server certificate verify properly or the
+ # connection will fail.
+ clientContext.set_verify(
+ VERIFY_PEER,
+ lambda conn, cert, errno, depth, preverify_ok: preverify_ok)
+
+ clientSSL = Connection(clientContext, client)
+ clientSSL.set_connect_state()
+
+ serverContext = Context(TLSv1_METHOD)
+ serverContext.use_certificate(
+ load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
+ serverContext.use_privatekey(
+ load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
+
+ serverSSL = Connection(serverContext, server)
+ serverSSL.set_accept_state()
+
+ # Without load_verify_locations above, the handshake
+ # will fail:
+ # Error: [('SSL routines', 'SSL3_GET_SERVER_CERTIFICATE',
+ # 'certificate verify failed')]
+ handshake(clientSSL, serverSSL)
+
+ cert = clientSSL.get_peer_certificate()
+ assert cert.get_subject().CN == 'Testing Root CA'
+
+ def _load_verify_cafile(self, cafile):
+ """
+ Verify that if path to a file containing a certificate is passed to
+ `Context.load_verify_locations` for the ``cafile`` parameter, that
+ certificate is used as a trust root for the purposes of verifying
+ connections created using that `Context`.
+ """
+ with open(cafile, 'w') as fObj:
+ fObj.write(cleartextCertificatePEM.decode('ascii'))
+
+ self._load_verify_locations_test(cafile)
+
+ def test_load_verify_bytes_cafile(self, tmpfile):
+ """
+ `Context.load_verify_locations` accepts a file name as a `bytes`
+ instance and uses the certificates within for verification purposes.
+ """
+ cafile = tmpfile + NON_ASCII.encode(getfilesystemencoding())
+ self._load_verify_cafile(cafile)
+
+ def test_load_verify_unicode_cafile(self, tmpfile):
+ """
+ `Context.load_verify_locations` accepts a file name as a `unicode`
+ instance and uses the certificates within for verification purposes.
+ """
+ self._load_verify_cafile(
+ tmpfile.decode(getfilesystemencoding()) + NON_ASCII
+ )
+
+ def test_load_verify_invalid_file(self, tmpfile):
+ """
+ `Context.load_verify_locations` raises `Error` when passed a
+ non-existent cafile.
+ """
+ clientContext = Context(TLSv1_METHOD)
+ with pytest.raises(Error):
+ clientContext.load_verify_locations(tmpfile)
+
+ def _load_verify_directory_locations_capath(self, capath):
+ """
+ Verify that if path to a directory containing certificate files is
+ passed to ``Context.load_verify_locations`` for the ``capath``
+ parameter, those certificates are used as trust roots for the purposes
+ of verifying connections created using that ``Context``.
+ """
+ makedirs(capath)
+ # 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 [b'c7adac82.0', b'c3705638.0']:
+ cafile = join_bytes_or_unicode(capath, name)
+ with open(cafile, 'w') as fObj:
+ fObj.write(cleartextCertificatePEM.decode('ascii'))
+
+ self._load_verify_locations_test(None, capath)
+
+ def test_load_verify_directory_bytes_capath(self, tmpfile):
+ """
+ `Context.load_verify_locations` accepts a directory name as a `bytes`
+ instance and uses the certificates within for verification purposes.
+ """
+ self._load_verify_directory_locations_capath(
+ tmpfile + NON_ASCII.encode(getfilesystemencoding())
+ )
+
+ def test_load_verify_directory_unicode_capath(self, tmpfile):
+ """
+ `Context.load_verify_locations` accepts a directory name as a `unicode`
+ instance and uses the certificates within for verification purposes.
+ """
+ self._load_verify_directory_locations_capath(
+ tmpfile.decode(getfilesystemencoding()) + NON_ASCII
+ )
+
+ def test_load_verify_locations_wrong_args(self):
+ """
+ `Context.load_verify_locations` raises `TypeError` if with non-`str`
+ arguments.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ context.load_verify_locations(object())
+ with pytest.raises(TypeError):
+ context.load_verify_locations(object(), object())
+
+ @pytest.mark.skipif(
+ not platform.startswith("linux"),
+ reason="Loading fallback paths is a linux-specific behavior to "
+ "accommodate pyca/cryptography manylinux1 wheels"
+ )
+ def test_fallback_default_verify_paths(self, monkeypatch):
+ """
+ Test that we load certificates successfully on linux from the fallback
+ path. To do this we set the _CRYPTOGRAPHY_MANYLINUX1_CA_FILE and
+ _CRYPTOGRAPHY_MANYLINUX1_CA_DIR vars to be equal to whatever the
+ current OpenSSL default is and we disable
+ SSL_CTX_SET_default_verify_paths so that it can't find certs unless
+ it loads via fallback.
+ """
+ context = Context(TLSv1_METHOD)
+ monkeypatch.setattr(
+ _lib, "SSL_CTX_set_default_verify_paths", lambda x: 1
+ )
+ monkeypatch.setattr(
+ SSL,
+ "_CRYPTOGRAPHY_MANYLINUX1_CA_FILE",
+ _ffi.string(_lib.X509_get_default_cert_file())
+ )
+ monkeypatch.setattr(
+ SSL,
+ "_CRYPTOGRAPHY_MANYLINUX1_CA_DIR",
+ _ffi.string(_lib.X509_get_default_cert_dir())
+ )
+ context.set_default_verify_paths()
+ store = context.get_cert_store()
+ sk_obj = _lib.X509_STORE_get0_objects(store._store)
+ assert sk_obj != _ffi.NULL
+ num = _lib.sk_X509_OBJECT_num(sk_obj)
+ assert num != 0
+
+ def test_check_env_vars(self, monkeypatch):
+ """
+ Test that we return True/False appropriately if the env vars are set.
+ """
+ context = Context(TLSv1_METHOD)
+ dir_var = "CUSTOM_DIR_VAR"
+ file_var = "CUSTOM_FILE_VAR"
+ assert context._check_env_vars_set(dir_var, file_var) is False
+ monkeypatch.setenv(dir_var, "value")
+ monkeypatch.setenv(file_var, "value")
+ assert context._check_env_vars_set(dir_var, file_var) is True
+ assert context._check_env_vars_set(dir_var, file_var) is True
+
+ def test_verify_no_fallback_if_env_vars_set(self, monkeypatch):
+ """
+ Test that we don't use the fallback path if env vars are set.
+ """
+ context = Context(TLSv1_METHOD)
+ monkeypatch.setattr(
+ _lib, "SSL_CTX_set_default_verify_paths", lambda x: 1
+ )
+ dir_env_var = _ffi.string(
+ _lib.X509_get_default_cert_dir_env()
+ ).decode("ascii")
+ file_env_var = _ffi.string(
+ _lib.X509_get_default_cert_file_env()
+ ).decode("ascii")
+ monkeypatch.setenv(dir_env_var, "value")
+ monkeypatch.setenv(file_env_var, "value")
+ context.set_default_verify_paths()
+
+ monkeypatch.setattr(
+ context,
+ "_fallback_default_verify_paths",
+ raiser(SystemError)
+ )
+ context.set_default_verify_paths()
+
+ @pytest.mark.skipif(
+ platform == "win32",
+ reason="set_default_verify_paths appears not to work on Windows. "
+ "See LP#404343 and LP#404344."
+ )
+ @pytest.mark.network
+ def test_set_default_verify_paths(self):
+ """
+ `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 the CAs in the platform CA location. Getting one of those
+ # costs money. Fortunately (or unfortunately, depending on your
+ # perspective), it's easy to think of a public server on the
+ # internet which has such a certificate. Connecting to the network
+ # in a unit test is bad, but it's the only way I can think of to
+ # really test this. -exarkun
+ context = Context(SSLv23_METHOD)
+ context.set_default_verify_paths()
+ context.set_verify(
+ VERIFY_PEER,
+ lambda conn, cert, errno, depth, preverify_ok: preverify_ok)
+
+ client = socket()
+ client.connect(("encrypted.google.com", 443))
+ clientSSL = Connection(context, client)
+ clientSSL.set_connect_state()
+ clientSSL.set_tlsext_host_name(b"encrypted.google.com")
+ clientSSL.do_handshake()
+ clientSSL.send(b"GET / HTTP/1.0\r\n\r\n")
+ assert clientSSL.recv(1024)
+
+ def test_fallback_path_is_not_file_or_dir(self):
+ """
+ Test that when passed empty arrays or paths that do not exist no
+ errors are raised.
+ """
+ context = Context(TLSv1_METHOD)
+ context._fallback_default_verify_paths([], [])
+ context._fallback_default_verify_paths(
+ ["/not/a/file"], ["/not/a/dir"]
+ )
+
+ def test_add_extra_chain_cert_invalid_cert(self):
+ """
+ `Context.add_extra_chain_cert` raises `TypeError` if called with an
+ object which is not an instance of `X509`.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ context.add_extra_chain_cert(object())
+
+ def _handshake_test(self, serverContext, clientContext):
+ """
+ Verify that a client and server created with the given contexts can
+ successfully handshake and communicate.
+ """
+ serverSocket, clientSocket = socket_pair()
+
+ server = Connection(serverContext, serverSocket)
+ server.set_accept_state()
+
+ client = Connection(clientContext, clientSocket)
+ client.set_connect_state()
+
+ # Make them talk to each other.
+ # interact_in_memory(client, server)
+ for _ in range(3):
+ for s in [client, server]:
+ try:
+ s.do_handshake()
+ except WantReadError:
+ pass
+
+ def test_set_verify_callback_connection_argument(self):
+ """
+ The first argument passed to the verify callback is the
+ `Connection` instance for which verification is taking place.
+ """
+ serverContext = Context(TLSv1_METHOD)
+ serverContext.use_privatekey(
+ load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
+ serverContext.use_certificate(
+ load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
+ serverConnection = Connection(serverContext, None)
+
+ class VerifyCallback(object):
+ def callback(self, connection, *args):
+ self.connection = connection
+ return 1
+
+ verify = VerifyCallback()
+ clientContext = Context(TLSv1_METHOD)
+ clientContext.set_verify(VERIFY_PEER, verify.callback)
+ clientConnection = Connection(clientContext, None)
+ clientConnection.set_connect_state()
+
+ handshake_in_memory(clientConnection, serverConnection)
+
+ assert verify.connection is clientConnection
+
+ def test_x509_in_verify_works(self):
+ """
+ We had a bug where the X509 cert instantiated in the callback wrapper
+ didn't __init__ so it was missing objects needed when calling
+ get_subject. This test sets up a handshake where we call get_subject
+ on the cert provided to the verify callback.
+ """
+ serverContext = Context(TLSv1_METHOD)
+ serverContext.use_privatekey(
+ load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
+ serverContext.use_certificate(
+ load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
+ serverConnection = Connection(serverContext, None)
+
+ def verify_cb_get_subject(conn, cert, errnum, depth, ok):
+ assert cert.get_subject()
+ return 1
+
+ clientContext = Context(TLSv1_METHOD)
+ clientContext.set_verify(VERIFY_PEER, verify_cb_get_subject)
+ clientConnection = Connection(clientContext, None)
+ clientConnection.set_connect_state()
+
+ handshake_in_memory(clientConnection, serverConnection)
+
+ def test_set_verify_callback_exception(self):
+ """
+ If the verify callback passed to `Context.set_verify` raises an
+ exception, verification fails and the exception is propagated to the
+ caller of `Connection.do_handshake`.
+ """
+ serverContext = Context(TLSv1_2_METHOD)
+ serverContext.use_privatekey(
+ load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM))
+ serverContext.use_certificate(
+ load_certificate(FILETYPE_PEM, cleartextCertificatePEM))
+
+ clientContext = Context(TLSv1_2_METHOD)
+
+ def verify_callback(*args):
+ raise Exception("silly verify failure")
+ clientContext.set_verify(VERIFY_PEER, verify_callback)
+
+ with pytest.raises(Exception) as exc:
+ self._handshake_test(serverContext, clientContext)
+
+ assert "silly verify failure" == str(exc.value)
+
+ def test_add_extra_chain_cert(self, tmpdir):
+ """
+ `Context.add_extra_chain_cert` accepts an `X509`
+ instance to add to the certificate chain.
+
+ See `_create_certificate_chain` for the details of the
+ certificate chain tested.
+
+ The chain is tested by starting a server with scert and connecting
+ to it with a client which trusts cacert and requires verification to
+ succeed.
+ """
+ chain = _create_certificate_chain()
+ [(cakey, cacert), (ikey, icert), (skey, scert)] = chain
+
+ # Dump the CA certificate to a file because that's the only way to load
+ # it as a trusted CA in the client context.
+ for cert, name in [(cacert, 'ca.pem'),
+ (icert, 'i.pem'),
+ (scert, 's.pem')]:
+ with tmpdir.join(name).open('w') as f:
+ f.write(dump_certificate(FILETYPE_PEM, cert).decode('ascii'))
+
+ for key, name in [(cakey, 'ca.key'),
+ (ikey, 'i.key'),
+ (skey, 's.key')]:
+ with tmpdir.join(name).open('w') as f:
+ f.write(dump_privatekey(FILETYPE_PEM, key).decode('ascii'))
+
+ # Create the server context
+ serverContext = Context(TLSv1_METHOD)
+ serverContext.use_privatekey(skey)
+ serverContext.use_certificate(scert)
+ # The client already has cacert, we only need to give them icert.
+ serverContext.add_extra_chain_cert(icert)
+
+ # Create the client
+ clientContext = Context(TLSv1_METHOD)
+ clientContext.set_verify(
+ VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb)
+ clientContext.load_verify_locations(str(tmpdir.join("ca.pem")))
+
+ # Try it out.
+ self._handshake_test(serverContext, clientContext)
+
+ def _use_certificate_chain_file_test(self, certdir):
+ """
+ Verify that `Context.use_certificate_chain_file` reads a
+ certificate chain from a specified file.
+
+ The chain is tested by starting a server with scert and connecting to
+ it with a client which trusts cacert and requires verification to
+ succeed.
+ """
+ chain = _create_certificate_chain()
+ [(cakey, cacert), (ikey, icert), (skey, scert)] = chain
+
+ makedirs(certdir)
+
+ chainFile = join_bytes_or_unicode(certdir, "chain.pem")
+ caFile = join_bytes_or_unicode(certdir, "ca.pem")
+
+ # Write out the chain file.
+ with open(chainFile, 'wb') as fObj:
+ # Most specific to least general.
+ fObj.write(dump_certificate(FILETYPE_PEM, scert))
+ fObj.write(dump_certificate(FILETYPE_PEM, icert))
+ fObj.write(dump_certificate(FILETYPE_PEM, cacert))
+
+ with open(caFile, 'w') as fObj:
+ fObj.write(dump_certificate(FILETYPE_PEM, cacert).decode('ascii'))
+
+ serverContext = Context(TLSv1_METHOD)
+ serverContext.use_certificate_chain_file(chainFile)
+ serverContext.use_privatekey(skey)
+
+ clientContext = Context(TLSv1_METHOD)
+ clientContext.set_verify(
+ VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb)
+ clientContext.load_verify_locations(caFile)
+
+ self._handshake_test(serverContext, clientContext)
+
+ def test_use_certificate_chain_file_bytes(self, tmpfile):
+ """
+ ``Context.use_certificate_chain_file`` accepts the name of a file (as
+ an instance of ``bytes``) to specify additional certificates to use to
+ construct and verify a trust chain.
+ """
+ self._use_certificate_chain_file_test(
+ tmpfile + NON_ASCII.encode(getfilesystemencoding())
+ )
+
+ def test_use_certificate_chain_file_unicode(self, tmpfile):
+ """
+ ``Context.use_certificate_chain_file`` accepts the name of a file (as
+ an instance of ``unicode``) to specify additional certificates to use
+ to construct and verify a trust chain.
+ """
+ self._use_certificate_chain_file_test(
+ tmpfile.decode(getfilesystemencoding()) + NON_ASCII
+ )
+
+ def test_use_certificate_chain_file_wrong_args(self):
+ """
+ `Context.use_certificate_chain_file` raises `TypeError` if passed a
+ non-byte string single argument.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ context.use_certificate_chain_file(object())
+
+ def test_use_certificate_chain_file_missing_file(self, tmpfile):
+ """
+ `Context.use_certificate_chain_file` raises `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)
+ with pytest.raises(Error):
+ context.use_certificate_chain_file(tmpfile)
+
+ def test_set_verify_mode(self):
+ """
+ `Context.get_verify_mode` returns the verify mode flags previously
+ passed to `Context.set_verify`.
+ """
+ context = Context(TLSv1_METHOD)
+ assert context.get_verify_mode() == 0
+ context.set_verify(
+ VERIFY_PEER | VERIFY_CLIENT_ONCE, lambda *args: None)
+ assert context.get_verify_mode() == (VERIFY_PEER | VERIFY_CLIENT_ONCE)
+
+ @skip_if_py3
+ def test_set_verify_mode_long(self):
+ """
+ On Python 2 `Context.set_verify_mode` accepts values of type `long`
+ as well as `int`.
+ """
+ context = Context(TLSv1_METHOD)
+ assert context.get_verify_mode() == 0
+ context.set_verify(
+ long(VERIFY_PEER | VERIFY_CLIENT_ONCE), lambda *args: None
+ ) # pragma: nocover
+ assert context.get_verify_mode() == (VERIFY_PEER | VERIFY_CLIENT_ONCE)
+
+ @pytest.mark.parametrize('mode', [None, 1.0, object(), 'mode'])
+ def test_set_verify_wrong_mode_arg(self, mode):
+ """
+ `Context.set_verify` raises `TypeError` if the first argument is
+ not an integer.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ context.set_verify(mode=mode, callback=lambda *args: None)
+
+ @pytest.mark.parametrize('callback', [None, 1.0, 'mode', ('foo', 'bar')])
+ def test_set_verify_wrong_callable_arg(self, callback):
+ """
+ `Context.set_verify` raises `TypeError` if the second argument
+ is not callable.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ context.set_verify(mode=VERIFY_PEER, callback=callback)
+
+ def test_load_tmp_dh_wrong_args(self):
+ """
+ `Context.load_tmp_dh` raises `TypeError` if called with a
+ non-`str` argument.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ context.load_tmp_dh(object())
+
+ def test_load_tmp_dh_missing_file(self):
+ """
+ `Context.load_tmp_dh` raises `OpenSSL.SSL.Error` if the
+ specified file does not exist.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(Error):
+ context.load_tmp_dh(b"hello")
+
+ def _load_tmp_dh_test(self, dhfilename):
+ """
+ Verify that calling ``Context.load_tmp_dh`` with the given filename
+ does not raise an exception.
+ """
+ context = Context(TLSv1_METHOD)
+ with open(dhfilename, "w") as dhfile:
+ dhfile.write(dhparam)
+
+ context.load_tmp_dh(dhfilename)
+ # XXX What should I assert here? -exarkun
+
+ def test_load_tmp_dh_bytes(self, tmpfile):
+ """
+ `Context.load_tmp_dh` loads Diffie-Hellman parameters from the
+ specified file (given as ``bytes``).
+ """
+ self._load_tmp_dh_test(
+ tmpfile + NON_ASCII.encode(getfilesystemencoding()),
+ )
+
+ def test_load_tmp_dh_unicode(self, tmpfile):
+ """
+ `Context.load_tmp_dh` loads Diffie-Hellman parameters from the
+ specified file (given as ``unicode``).
+ """
+ self._load_tmp_dh_test(
+ tmpfile.decode(getfilesystemencoding()) + NON_ASCII,
+ )
+
+ def test_set_tmp_ecdh(self):
+ """
+ `Context.set_tmp_ecdh` sets the elliptic curve for Diffie-Hellman to
+ the specified curve.
+ """
+ context = Context(TLSv1_METHOD)
+ for curve in get_elliptic_curves():
+ if curve.name.startswith(u"Oakley-"):
+ # Setting Oakley-EC2N-4 and Oakley-EC2N-3 adds
+ # ('bignum routines', 'BN_mod_inverse', 'no inverse') to the
+ # error queue on OpenSSL 1.0.2.
+ continue
+ # The only easily "assertable" thing is that it does not raise an
+ # exception.
+ context.set_tmp_ecdh(curve)
+
+ def test_set_session_cache_mode_wrong_args(self):
+ """
+ `Context.set_session_cache_mode` raises `TypeError` if called with
+ a non-integer argument.
+ called with other than one integer argument.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ context.set_session_cache_mode(object())
+
+ def test_session_cache_mode(self):
+ """
+ `Context.set_session_cache_mode` specifies how sessions are cached.
+ The setting can be retrieved via `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)
+ assert SESS_CACHE_OFF == off
+ assert SESS_CACHE_BOTH == context.get_session_cache_mode()
+
+ @skip_if_py3
+ def test_session_cache_mode_long(self):
+ """
+ On Python 2 `Context.set_session_cache_mode` accepts values
+ of type `long` as well as `int`.
+ """
+ context = Context(TLSv1_METHOD)
+ context.set_session_cache_mode(long(SESS_CACHE_BOTH))
+ assert SESS_CACHE_BOTH == context.get_session_cache_mode()
+
+ def test_get_cert_store(self):
+ """
+ `Context.get_cert_store` returns a `X509Store` instance.
+ """
+ context = Context(TLSv1_METHOD)
+ store = context.get_cert_store()
+ assert isinstance(store, X509Store)
+
+ def test_set_tlsext_use_srtp_not_bytes(self):
+ """
+ `Context.set_tlsext_use_srtp' enables negotiating SRTP keying material.
+
+ It raises a TypeError if the list of profiles is not a byte string.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ context.set_tlsext_use_srtp(text_type('SRTP_AES128_CM_SHA1_80'))
+
+ def test_set_tlsext_use_srtp_invalid_profile(self):
+ """
+ `Context.set_tlsext_use_srtp' enables negotiating SRTP keying material.
+
+ It raises an Error if the call to OpenSSL fails.
+ """
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(Error):
+ context.set_tlsext_use_srtp(b'SRTP_BOGUS')
+
+ def test_set_tlsext_use_srtp_valid(self):
+ """
+ `Context.set_tlsext_use_srtp' enables negotiating SRTP keying material.
+
+ It does not return anything.
+ """
+ context = Context(TLSv1_METHOD)
+ assert context.set_tlsext_use_srtp(b'SRTP_AES128_CM_SHA1_80') is None
+
+
+class TestServerNameCallback(object):
+ """
+ Tests for `Context.set_tlsext_servername_callback` and its
+ interaction with `Connection`.
+ """
+ def test_old_callback_forgotten(self):
+ """
+ If `Context.set_tlsext_servername_callback` is used to specify
+ a new callback, the one it replaces is dereferenced.
+ """
+ def callback(connection): # pragma: no cover
+ pass
+
+ def replacement(connection): # pragma: no cover
+ pass
+
+ context = Context(TLSv1_METHOD)
+ context.set_tlsext_servername_callback(callback)
+
+ tracker = ref(callback)
+ 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()
+
+ callback = tracker()
+ if callback is not None:
+ referrers = get_referrers(callback)
+ if len(referrers) > 1: # pragma: nocover
+ pytest.fail("Some references remain: %r" % (referrers,))
+
+ def test_no_servername(self):
+ """
+ When a client specifies no server name, the callback passed to
+ `Context.set_tlsext_servername_callback` is invoked and the
+ result of `Connection.get_servername` is `None`.
+ """
+ args = []
+
+ def servername(conn):
+ args.append((conn, conn.get_servername()))
+ context = Context(TLSv1_METHOD)
+ context.set_tlsext_servername_callback(servername)
+
+ # Lose our reference to it. The Context is responsible for keeping it
+ # alive now.
+ del servername
+ collect()
+
+ # Necessary to actually accept the connection
+ context.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
+ context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem))
+
+ # Do a little connection to trigger the logic
+ server = Connection(context, None)
+ server.set_accept_state()
+
+ client = Connection(Context(TLSv1_METHOD), None)
+ client.set_connect_state()
+
+ interact_in_memory(server, client)
+
+ assert args == [(server, None)]
+
+ def test_servername(self):
+ """
+ When a client specifies a server name in its hello message, the
+ callback passed to `Contexts.set_tlsext_servername_callback` is
+ invoked and the result of `Connection.get_servername` is that
+ server name.
+ """
+ args = []
+
+ def servername(conn):
+ args.append((conn, conn.get_servername()))
+ context = Context(TLSv1_METHOD)
+ context.set_tlsext_servername_callback(servername)
+
+ # Necessary to actually accept the connection
+ context.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
+ context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem))
+
+ # Do a little connection to trigger the logic
+ server = Connection(context, None)
+ server.set_accept_state()
+
+ client = Connection(Context(TLSv1_METHOD), None)
+ client.set_connect_state()
+ client.set_tlsext_host_name(b"foo1.example.com")
+
+ interact_in_memory(server, client)
+
+ assert args == [(server, b"foo1.example.com")]
+
+
+class TestNextProtoNegotiation(object):
+ """
+ Test for Next Protocol Negotiation in PyOpenSSL.
+ """
+ def test_npn_success(self):
+ """
+ Tests that clients and servers that agree on the negotiated next
+ protocol can correct establish a connection, and that the agreed
+ protocol is reported by the connections.
+ """
+ advertise_args = []
+ select_args = []
+
+ def advertise(conn):
+ advertise_args.append((conn,))
+ return [b'http/1.1', b'spdy/2']
+
+ def select(conn, options):
+ select_args.append((conn, options))
+ return b'spdy/2'
+
+ server_context = Context(TLSv1_METHOD)
+ server_context.set_npn_advertise_callback(advertise)
+
+ client_context = Context(TLSv1_METHOD)
+ client_context.set_npn_select_callback(select)
+
+ # Necessary to actually accept the connection
+ server_context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem))
+ server_context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem))
+
+ # Do a little connection to trigger the logic
+ server = Connection(server_context, None)
+ server.set_accept_state()
+
+ client = Connection(client_context, None)
+ client.set_connect_state()
+
+ interact_in_memory(server, client)
+
+ assert advertise_args == [(server,)]
+ assert select_args == [(client, [b'http/1.1', b'spdy/2'])]
+
+ assert server.get_next_proto_negotiated() == b'spdy/2'
+ assert client.get_next_proto_negotiated() == b'spdy/2'
+
+ def test_npn_client_fail(self):
+ """
+ Tests that when clients and servers cannot agree on what protocol
+ to use next that the TLS connection does not get established.
+ """
+ advertise_args = []
+ select_args = []
+
+ def advertise(conn):
+ advertise_args.append((conn,))
+ return [b'http/1.1', b'spdy/2']
+
+ def select(conn, options):
+ select_args.append((conn, options))
+ return b''
+
+ server_context = Context(TLSv1_METHOD)
+ server_context.set_npn_advertise_callback(advertise)
+
+ client_context = Context(TLSv1_METHOD)
+ client_context.set_npn_select_callback(select)
+
+ # Necessary to actually accept the connection
+ server_context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem))
+ server_context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem))
+
+ # Do a little connection to trigger the logic
+ server = Connection(server_context, None)
+ server.set_accept_state()
+
+ client = Connection(client_context, None)
+ client.set_connect_state()
+
+ # If the client doesn't return anything, the connection will fail.
+ with pytest.raises(Error):
+ interact_in_memory(server, client)
+
+ assert advertise_args == [(server,)]
+ assert select_args == [(client, [b'http/1.1', b'spdy/2'])]
+
+ def test_npn_select_error(self):
+ """
+ Test that we can handle exceptions in the select callback. If
+ select fails it should be fatal to the connection.
+ """
+ advertise_args = []
+
+ def advertise(conn):
+ advertise_args.append((conn,))
+ return [b'http/1.1', b'spdy/2']
+
+ def select(conn, options):
+ raise TypeError
+
+ server_context = Context(TLSv1_METHOD)
+ server_context.set_npn_advertise_callback(advertise)
+
+ client_context = Context(TLSv1_METHOD)
+ client_context.set_npn_select_callback(select)
+
+ # Necessary to actually accept the connection
+ server_context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem))
+ server_context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem))
+
+ # Do a little connection to trigger the logic
+ server = Connection(server_context, None)
+ server.set_accept_state()
+
+ client = Connection(client_context, None)
+ client.set_connect_state()
+
+ # If the callback throws an exception it should be raised here.
+ with pytest.raises(TypeError):
+ interact_in_memory(server, client)
+ assert advertise_args == [(server,), ]
+
+ def test_npn_advertise_error(self):
+ """
+ Test that we can handle exceptions in the advertise callback. If
+ advertise fails no NPN is advertised to the client.
+ """
+ select_args = []
+
+ def advertise(conn):
+ raise TypeError
+
+ def select(conn, options): # pragma: nocover
+ """
+ Assert later that no args are actually appended.
+ """
+ select_args.append((conn, options))
+ return b''
+
+ server_context = Context(TLSv1_METHOD)
+ server_context.set_npn_advertise_callback(advertise)
+
+ client_context = Context(TLSv1_METHOD)
+ client_context.set_npn_select_callback(select)
+
+ # Necessary to actually accept the connection
+ server_context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem))
+ server_context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem))
+
+ # Do a little connection to trigger the logic
+ server = Connection(server_context, None)
+ server.set_accept_state()
+
+ client = Connection(client_context, None)
+ client.set_connect_state()
+
+ # If the client doesn't return anything, the connection will fail.
+ with pytest.raises(TypeError):
+ interact_in_memory(server, client)
+ assert select_args == []
+
+
+class TestApplicationLayerProtoNegotiation(object):
+ """
+ Tests for ALPN in PyOpenSSL.
+ """
+ # Skip tests on versions that don't support ALPN.
+ if _lib.Cryptography_HAS_ALPN:
+
+ def test_alpn_success(self):
+ """
+ Clients and servers that agree on the negotiated ALPN protocol can
+ correct establish a connection, and the agreed protocol is reported
+ by the connections.
+ """
+ select_args = []
+
+ def select(conn, options):
+ select_args.append((conn, options))
+ return b'spdy/2'
+
+ client_context = Context(TLSv1_METHOD)
+ client_context.set_alpn_protos([b'http/1.1', b'spdy/2'])
+
+ server_context = Context(TLSv1_METHOD)
+ server_context.set_alpn_select_callback(select)
+
+ # Necessary to actually accept the connection
+ server_context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem))
+ server_context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem))
+
+ # Do a little connection to trigger the logic
+ server = Connection(server_context, None)
+ server.set_accept_state()
+
+ client = Connection(client_context, None)
+ client.set_connect_state()
+
+ interact_in_memory(server, client)
+
+ assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
+
+ assert server.get_alpn_proto_negotiated() == b'spdy/2'
+ assert client.get_alpn_proto_negotiated() == b'spdy/2'
+
+ def test_alpn_set_on_connection(self):
+ """
+ The same as test_alpn_success, but setting the ALPN protocols on
+ the connection rather than the context.
+ """
+ select_args = []
+
+ def select(conn, options):
+ select_args.append((conn, options))
+ return b'spdy/2'
+
+ # Setup the client context but don't set any ALPN protocols.
+ client_context = Context(TLSv1_METHOD)
+
+ server_context = Context(TLSv1_METHOD)
+ server_context.set_alpn_select_callback(select)
+
+ # Necessary to actually accept the connection
+ server_context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem))
+ server_context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem))
+
+ # Do a little connection to trigger the logic
+ server = Connection(server_context, None)
+ server.set_accept_state()
+
+ # Set the ALPN protocols on the client connection.
+ client = Connection(client_context, None)
+ client.set_alpn_protos([b'http/1.1', b'spdy/2'])
+ client.set_connect_state()
+
+ interact_in_memory(server, client)
+
+ assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
+
+ assert server.get_alpn_proto_negotiated() == b'spdy/2'
+ assert client.get_alpn_proto_negotiated() == b'spdy/2'
+
+ def test_alpn_server_fail(self):
+ """
+ When clients and servers cannot agree on what protocol to use next
+ the TLS connection does not get established.
+ """
+ select_args = []
+
+ def select(conn, options):
+ select_args.append((conn, options))
+ return b''
+
+ client_context = Context(TLSv1_METHOD)
+ client_context.set_alpn_protos([b'http/1.1', b'spdy/2'])
+
+ server_context = Context(TLSv1_METHOD)
+ server_context.set_alpn_select_callback(select)
+
+ # Necessary to actually accept the connection
+ server_context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem))
+ server_context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem))
+
+ # Do a little connection to trigger the logic
+ server = Connection(server_context, None)
+ server.set_accept_state()
+
+ client = Connection(client_context, None)
+ client.set_connect_state()
+
+ # If the client doesn't return anything, the connection will fail.
+ with pytest.raises(Error):
+ interact_in_memory(server, client)
+
+ assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
+
+ def test_alpn_no_server(self):
+ """
+ When clients and servers cannot agree on what protocol to use next
+ because the server doesn't offer ALPN, no protocol is negotiated.
+ """
+ client_context = Context(TLSv1_METHOD)
+ client_context.set_alpn_protos([b'http/1.1', b'spdy/2'])
+
+ server_context = Context(TLSv1_METHOD)
+
+ # Necessary to actually accept the connection
+ server_context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem))
+ server_context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem))
+
+ # Do a little connection to trigger the logic
+ server = Connection(server_context, None)
+ server.set_accept_state()
+
+ client = Connection(client_context, None)
+ client.set_connect_state()
+
+ # Do the dance.
+ interact_in_memory(server, client)
+
+ assert client.get_alpn_proto_negotiated() == b''
+
+ def test_alpn_callback_exception(self):
+ """
+ We can handle exceptions in the ALPN select callback.
+ """
+ select_args = []
+
+ def select(conn, options):
+ select_args.append((conn, options))
+ raise TypeError()
+
+ client_context = Context(TLSv1_METHOD)
+ client_context.set_alpn_protos([b'http/1.1', b'spdy/2'])
+
+ server_context = Context(TLSv1_METHOD)
+ server_context.set_alpn_select_callback(select)
+
+ # Necessary to actually accept the connection
+ server_context.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem))
+ server_context.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem))
+
+ # Do a little connection to trigger the logic
+ server = Connection(server_context, None)
+ server.set_accept_state()
+
+ client = Connection(client_context, None)
+ client.set_connect_state()
+
+ with pytest.raises(TypeError):
+ interact_in_memory(server, client)
+ assert select_args == [(server, [b'http/1.1', b'spdy/2'])]
+
+ else:
+ # No ALPN.
+ def test_alpn_not_implemented(self):
+ """
+ If ALPN is not in OpenSSL, we should raise NotImplementedError.
+ """
+ # Test the context methods first.
+ context = Context(TLSv1_METHOD)
+ with pytest.raises(NotImplementedError):
+ context.set_alpn_protos(None)
+ with pytest.raises(NotImplementedError):
+ context.set_alpn_select_callback(None)
+
+ # Now test a connection.
+ conn = Connection(context)
+ with pytest.raises(NotImplementedError):
+ conn.set_alpn_protos(None)
+
+
+class TestSession(object):
+ """
+ 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()
+ assert isinstance(new_session, Session)
+
+
+class TestConnection(object):
+ """
+ Unit tests for `OpenSSL.SSL.Connection`.
+ """
+ # XXX get_peer_certificate -> None
+ # XXX sock_shutdown
+ # XXX master_key -> TypeError
+ # XXX server_random -> TypeError
+ # XXX connect -> TypeError
+ # XXX connect_ex -> TypeError
+ # XXX set_connect_state -> TypeError
+ # XXX set_accept_state -> TypeError
+ # XXX do_handshake -> TypeError
+ # XXX bio_read -> TypeError
+ # XXX recv -> TypeError
+ # XXX send -> TypeError
+ # XXX bio_write -> TypeError
+
+ def test_type(self):
+ """
+ `Connection` and `ConnectionType` refer to the same type object and
+ can be used to create instances of that type.
+ """
+ assert Connection is ConnectionType
+ ctx = Context(TLSv1_METHOD)
+ assert is_consistent_type(Connection, 'Connection', ctx, None)
+
+ @pytest.mark.parametrize('bad_context', [object(), 'context', None, 1])
+ def test_wrong_args(self, bad_context):
+ """
+ `Connection.__init__` raises `TypeError` if called with a non-`Context`
+ instance argument.
+ """
+ with pytest.raises(TypeError):
+ Connection(bad_context)
+
+ def test_get_context(self):
+ """
+ `Connection.get_context` returns the `Context` instance used to
+ construct the `Connection` instance.
+ """
+ context = Context(TLSv1_METHOD)
+ connection = Connection(context, None)
+ assert connection.get_context() is context
+
+ def test_set_context_wrong_args(self):
+ """
+ `Connection.set_context` raises `TypeError` if called with a
+ non-`Context` instance argument.
+ """
+ ctx = Context(TLSv1_METHOD)
+ connection = Connection(ctx, None)
+ with pytest.raises(TypeError):
+ connection.set_context(object())
+ with pytest.raises(TypeError):
+ connection.set_context("hello")
+ with pytest.raises(TypeError):
+ connection.set_context(1)
+ assert ctx is connection.get_context()
+
+ def test_set_context(self):
+ """
+ `Connection.set_context` specifies a new `Context` instance to be
+ used for the connection.
+ """
+ original = Context(SSLv23_METHOD)
+ replacement = Context(TLSv1_METHOD)
+ connection = Connection(original, None)
+ connection.set_context(replacement)
+ assert replacement is connection.get_context()
+ # Lose our references to the contexts, just in case the Connection
+ # isn't properly managing its own contributions to their reference
+ # counts.
+ del original, replacement
+ collect()
+
+ def test_set_tlsext_host_name_wrong_args(self):
+ """
+ If `Connection.set_tlsext_host_name` is called with a non-byte string
+ argument or a byte string with an embedded NUL, `TypeError` is raised.
+ """
+ conn = Connection(Context(TLSv1_METHOD), None)
+ with pytest.raises(TypeError):
+ conn.set_tlsext_host_name(object())
+ with pytest.raises(TypeError):
+ conn.set_tlsext_host_name(b"with\0null")
+
+ if PY3:
+ # On Python 3.x, don't accidentally implicitly convert from text.
+ with pytest.raises(TypeError):
+ conn.set_tlsext_host_name(b"example.com".decode("ascii"))
+
+ def test_pending(self):
+ """
+ `Connection.pending` returns the number of bytes available for
+ immediate read.
+ """
+ connection = Connection(Context(TLSv1_METHOD), None)
+ assert connection.pending() == 0
+
+ def test_peek(self):
+ """
+ `Connection.recv` peeks into the connection if `socket.MSG_PEEK` is
+ passed.
+ """
+ server, client = loopback()
+ server.send(b'xy')
+ assert client.recv(2, MSG_PEEK) == b'xy'
+ assert client.recv(2, MSG_PEEK) == b'xy'
+ assert client.recv(2) == b'xy'
+
+ def test_connect_wrong_args(self):
+ """
+ `Connection.connect` raises `TypeError` if called with a non-address
+ argument.
+ """
+ connection = Connection(Context(TLSv1_METHOD), socket())
+ with pytest.raises(TypeError):
+ connection.connect(None)
+
+ def test_connect_refused(self):
+ """
+ `Connection.connect` raises `socket.error` if the underlying socket
+ connect method raises it.
+ """
+ client = socket()
+ context = Context(TLSv1_METHOD)
+ clientSSL = Connection(context, client)
+ # pytest.raises here doesn't work because of a bug in py.test on Python
+ # 2.6: https://github.com/pytest-dev/pytest/issues/988
+ try:
+ clientSSL.connect(("127.0.0.1", 1))
+ except error as e:
+ exc = e
+ assert exc.args[0] == ECONNREFUSED
+
+ def test_connect(self):
+ """
+ `Connection.connect` establishes a connection to the specified address.
+ """
+ port = socket()
+ port.bind(('', 0))
+ port.listen(3)
+
+ clientSSL = Connection(Context(TLSv1_METHOD), socket())
+ clientSSL.connect(('127.0.0.1', port.getsockname()[1]))
+ # XXX An assertion? Or something?
+
+ @pytest.mark.skipif(
+ platform == "darwin",
+ reason="connect_ex sometimes causes a kernel panic on OS X 10.6.4"
+ )
+ def test_connect_ex(self):
+ """
+ If there is a connection error, `Connection.connect_ex` returns the
+ errno instead of raising an exception.
+ """
+ port = socket()
+ port.bind(('', 0))
+ port.listen(3)
+
+ clientSSL = Connection(Context(TLSv1_METHOD), socket())
+ clientSSL.setblocking(False)
+ result = clientSSL.connect_ex(port.getsockname())
+ expected = (EINPROGRESS, EWOULDBLOCK)
+ assert result in expected
+
+ def test_accept(self):
+ """
+ `Connection.accept` accepts a pending connection attempt and returns a
+ tuple of a new `Connection` (the accepted client) and the address the
+ connection originated from.
+ """
+ ctx = Context(TLSv1_METHOD)
+ ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
+ ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
+ port = socket()
+ portSSL = Connection(ctx, port)
+ portSSL.bind(('', 0))
+ portSSL.listen(3)
+
+ clientSSL = Connection(Context(TLSv1_METHOD), socket())
+
+ # Calling portSSL.getsockname() here to get the server IP address
+ # sounds great, but frequently fails on Windows.
+ clientSSL.connect(('127.0.0.1', portSSL.getsockname()[1]))
+
+ serverSSL, address = portSSL.accept()
+
+ assert isinstance(serverSSL, Connection)
+ assert serverSSL.get_context() is ctx
+ assert address == clientSSL.getsockname()
+
+ def test_shutdown_wrong_args(self):
+ """
+ `Connection.set_shutdown` raises `TypeError` if called with arguments
+ other than integers.
+ """
+ connection = Connection(Context(TLSv1_METHOD), None)
+ with pytest.raises(TypeError):
+ connection.set_shutdown(None)
+
+ def test_shutdown(self):
+ """
+ `Connection.shutdown` performs an SSL-level connection shutdown.
+ """
+ server, client = loopback()
+ assert not server.shutdown()
+ assert server.get_shutdown() == SENT_SHUTDOWN
+ with pytest.raises(ZeroReturnError):
+ client.recv(1024)
+ assert client.get_shutdown() == RECEIVED_SHUTDOWN
+ client.shutdown()
+ assert client.get_shutdown() == (SENT_SHUTDOWN | RECEIVED_SHUTDOWN)
+ with pytest.raises(ZeroReturnError):
+ server.recv(1024)
+ assert server.get_shutdown() == (SENT_SHUTDOWN | RECEIVED_SHUTDOWN)
+
+ def test_shutdown_closed(self):
+ """
+ If the underlying socket is closed, `Connection.shutdown` propagates
+ the write error from the low level write call.
+ """
+ server, client = loopback()
+ server.sock_shutdown(2)
+ with pytest.raises(SysCallError) as exc:
+ server.shutdown()
+ if platform == "win32":
+ assert exc.value.args[0] == ESHUTDOWN
+ else:
+ assert exc.value.args[0] == EPIPE
+
+ def test_shutdown_truncated(self):
+ """
+ If the underlying connection is truncated, `Connection.shutdown`
+ raises an `Error`.
+ """
+ server_ctx = Context(TLSv1_METHOD)
+ client_ctx = Context(TLSv1_METHOD)
+ server_ctx.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem))
+ server_ctx.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem))
+ server = Connection(server_ctx, None)
+ client = Connection(client_ctx, None)
+ handshake_in_memory(client, server)
+ assert not server.shutdown()
+ with pytest.raises(WantReadError):
+ server.shutdown()
+ server.bio_shutdown()
+ with pytest.raises(Error):
+ server.shutdown()
+
+ def test_set_shutdown(self):
+ """
+ `Connection.set_shutdown` sets the state of the SSL connection
+ shutdown process.
+ """
+ connection = Connection(Context(TLSv1_METHOD), socket())
+ connection.set_shutdown(RECEIVED_SHUTDOWN)
+ assert connection.get_shutdown() == RECEIVED_SHUTDOWN
+
+ @skip_if_py3
+ def test_set_shutdown_long(self):
+ """
+ On Python 2 `Connection.set_shutdown` accepts an argument
+ of type `long` as well as `int`.
+ """
+ connection = Connection(Context(TLSv1_METHOD), socket())
+ connection.set_shutdown(long(RECEIVED_SHUTDOWN))
+ assert connection.get_shutdown() == RECEIVED_SHUTDOWN
+
+ def test_state_string(self):
+ """
+ `Connection.state_string` verbosely describes the current state of
+ the `Connection`.
+ """
+ server, client = socket_pair()
+ server = loopback_server_factory(server)
+ client = loopback_client_factory(client)
+
+ assert server.get_state_string() in [
+ b"before/accept initialization", b"before SSL initialization"
+ ]
+ assert client.get_state_string() in [
+ b"before/connect initialization", b"before SSL initialization"
+ ]
+
+ def test_app_data(self):
+ """
+ Any object can be set as app data by passing it to
+ `Connection.set_app_data` and later retrieved with
+ `Connection.get_app_data`.
+ """
+ conn = Connection(Context(TLSv1_METHOD), None)
+ assert None is conn.get_app_data()
+ app_data = object()
+ conn.set_app_data(app_data)
+ assert conn.get_app_data() is app_data
+
+ def test_makefile(self):
+ """
+ `Connection.makefile` is not implemented and calling that
+ method raises `NotImplementedError`.
+ """
+ conn = Connection(Context(TLSv1_METHOD), None)
+ with pytest.raises(NotImplementedError):
+ conn.makefile()
+
+ def test_get_certificate(self):
+ """
+ `Connection.get_certificate` returns the local certificate.
+ """
+ chain = _create_certificate_chain()
+ [(cakey, cacert), (ikey, icert), (skey, scert)] = chain
+
+ context = Context(TLSv1_METHOD)
+ context.use_certificate(scert)
+ client = Connection(context, None)
+ cert = client.get_certificate()
+ assert cert is not None
+ assert "Server Certificate" == cert.get_subject().CN
+
+ def test_get_certificate_none(self):
+ """
+ `Connection.get_certificate` returns the local certificate.
+
+ If there is no certificate, it returns None.
+ """
+ context = Context(TLSv1_METHOD)
+ client = Connection(context, None)
+ cert = client.get_certificate()
+ assert cert is None
+
+ def test_get_peer_cert_chain(self):
+ """
+ `Connection.get_peer_cert_chain` returns a list of certificates
+ which the connected server returned for the certification verification.
+ """
+ chain = _create_certificate_chain()
+ [(cakey, cacert), (ikey, icert), (skey, scert)] = chain
+
+ serverContext = Context(TLSv1_METHOD)
+ serverContext.use_privatekey(skey)
+ serverContext.use_certificate(scert)
+ serverContext.add_extra_chain_cert(icert)
+ serverContext.add_extra_chain_cert(cacert)
+ server = Connection(serverContext, None)
+ server.set_accept_state()
+
+ # Create the client
+ clientContext = Context(TLSv1_METHOD)
+ clientContext.set_verify(VERIFY_NONE, verify_cb)
+ client = Connection(clientContext, None)
+ client.set_connect_state()
+
+ interact_in_memory(client, server)
+
+ chain = client.get_peer_cert_chain()
+ assert len(chain) == 3
+ assert "Server Certificate" == chain[0].get_subject().CN
+ assert "Intermediate Certificate" == chain[1].get_subject().CN
+ assert "Authority Certificate" == chain[2].get_subject().CN
+
+ def test_get_peer_cert_chain_none(self):
+ """
+ `Connection.get_peer_cert_chain` returns `None` if the peer sends
+ no certificate chain.
+ """
+ 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, None)
+ server.set_accept_state()
+ client = Connection(Context(TLSv1_METHOD), None)
+ client.set_connect_state()
+ interact_in_memory(client, server)
+ assert None is server.get_peer_cert_chain()
+
+ def test_get_session_unconnected(self):
+ """
+ `Connection.get_session` returns `None` when used with an object
+ which has not been connected.
+ """
+ ctx = Context(TLSv1_METHOD)
+ server = Connection(ctx, None)
+ session = server.get_session()
+ assert None is session
+
+ def test_server_get_session(self):
+ """
+ On the server side of a connection, `Connection.get_session` returns a
+ `Session` instance representing the SSL session for that connection.
+ """
+ server, client = loopback()
+ session = server.get_session()
+ assert isinstance(session, Session)
+
+ def test_client_get_session(self):
+ """
+ On the client side of a connection, `Connection.get_session`
+ returns a `Session` instance representing the SSL session for
+ that connection.
+ """
+ server, client = loopback()
+ session = client.get_session()
+ assert isinstance(session, Session)
+
+ def test_set_session_wrong_args(self):
+ """
+ `Connection.set_session` raises `TypeError` if called with an object
+ that is not an instance of `Session`.
+ """
+ ctx = Context(TLSv1_METHOD)
+ connection = Connection(ctx, None)
+ with pytest.raises(TypeError):
+ connection.set_session(123)
+ with pytest.raises(TypeError):
+ connection.set_session("hello")
+ with pytest.raises(TypeError):
+ connection.set_session(object())
+
+ def test_client_set_session(self):
+ """
+ `Connection.set_session`, when used prior to a connection being
+ established, accepts a `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_2_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 = loopback(
+ server_factory=makeServer)
+ originalSession = originalClient.get_session()
+
+ def makeClient(socket):
+ client = loopback_client_factory(socket)
+ client.set_session(originalSession)
+ return client
+ resumedServer, resumedClient = loopback(
+ server_factory=makeServer,
+ client_factory=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!
+ assert originalServer.master_key() == resumedServer.master_key()
+
+ def test_set_session_wrong_method(self):
+ """
+ If `Connection.set_session` is passed a `Session` instance associated
+ with a context using a different SSL method than the `Connection`
+ is using, a `OpenSSL.SSL.Error` is raised.
+ """
+ # Make this work on both OpenSSL 1.0.0, which doesn't support TLSv1.2
+ # and also on OpenSSL 1.1.0 which doesn't support SSLv3. (SSL_ST_INIT
+ # is a way to check for 1.1.0)
+ if SSL_ST_INIT is None:
+ v1 = TLSv1_2_METHOD
+ v2 = TLSv1_METHOD
+ elif hasattr(_lib, "SSLv3_method"):
+ v1 = TLSv1_METHOD
+ v2 = SSLv3_METHOD
+ else:
+ pytest.skip("Test requires either OpenSSL 1.1.0 or SSLv3")
+
+ key = load_privatekey(FILETYPE_PEM, server_key_pem)
+ cert = load_certificate(FILETYPE_PEM, server_cert_pem)
+ ctx = Context(v1)
+ 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
+
+ def makeOriginalClient(socket):
+ client = Connection(Context(v1), socket)
+ client.set_connect_state()
+ return client
+
+ originalServer, originalClient = loopback(
+ server_factory=makeServer, client_factory=makeOriginalClient)
+ originalSession = originalClient.get_session()
+
+ def makeClient(socket):
+ # Intentionally use a different, incompatible method here.
+ client = Connection(Context(v2), socket)
+ client.set_connect_state()
+ client.set_session(originalSession)
+ return client
+
+ with pytest.raises(Error):
+ loopback(client_factory=makeClient, server_factory=makeServer)
+
+ def test_wantWriteError(self):
+ """
+ `Connection` methods which generate output raise
+ `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. Only write a single byte at a time so we can be sure we
+ # completely fill the buffer. Even though the socket API is allowed to
+ # signal a short write via its return value it seems this doesn't
+ # always happen on all platforms (FreeBSD and OS X particular) for the
+ # very last bit of available buffer space.
+ msg = b"x"
+ for i in range(1024 * 1024 * 64):
+ try:
+ client_socket.send(msg)
+ except error as e:
+ if e.errno == EWOULDBLOCK:
+ break
+ raise
+ else:
+ pytest.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()
+ with pytest.raises(WantWriteError):
+ conn.do_handshake()
+
+ # XXX want_read
+
+ def test_get_finished_before_connect(self):
+ """
+ `Connection.get_finished` returns `None` before TLS handshake
+ is completed.
+ """
+ ctx = Context(TLSv1_METHOD)
+ connection = Connection(ctx, None)
+ assert connection.get_finished() is None
+
+ def test_get_peer_finished_before_connect(self):
+ """
+ `Connection.get_peer_finished` returns `None` before TLS handshake
+ is completed.
+ """
+ ctx = Context(TLSv1_METHOD)
+ connection = Connection(ctx, None)
+ assert connection.get_peer_finished() is None
+
+ def test_get_finished(self):
+ """
+ `Connection.get_finished` method returns the TLS Finished message send
+ from client, or server. Finished messages are send during
+ TLS handshake.
+ """
+ server, client = loopback()
+
+ assert server.get_finished() is not None
+ assert len(server.get_finished()) > 0
+
+ def test_get_peer_finished(self):
+ """
+ `Connection.get_peer_finished` method returns the TLS Finished
+ message received from client, or server. Finished messages are send
+ during TLS handshake.
+ """
+ server, client = loopback()
+
+ assert server.get_peer_finished() is not None
+ assert len(server.get_peer_finished()) > 0
+
+ def test_tls_finished_message_symmetry(self):
+ """
+ The TLS Finished message send by server must be the TLS Finished
+ message received by client.
+
+ The TLS Finished message send by client must be the TLS Finished
+ message received by server.
+ """
+ server, client = loopback()
+
+ assert server.get_finished() == client.get_peer_finished()
+ assert client.get_finished() == server.get_peer_finished()
+
+ def test_get_cipher_name_before_connect(self):
+ """
+ `Connection.get_cipher_name` returns `None` if no connection
+ has been established.
+ """
+ ctx = Context(TLSv1_METHOD)
+ conn = Connection(ctx, None)
+ assert conn.get_cipher_name() is None
+
+ def test_get_cipher_name(self):
+ """
+ `Connection.get_cipher_name` returns a `unicode` string giving the
+ name of the currently used cipher.
+ """
+ server, client = loopback()
+ server_cipher_name, client_cipher_name = \
+ server.get_cipher_name(), client.get_cipher_name()
+
+ assert isinstance(server_cipher_name, text_type)
+ assert isinstance(client_cipher_name, text_type)
+
+ assert server_cipher_name == client_cipher_name
+
+ def test_get_cipher_version_before_connect(self):
+ """
+ `Connection.get_cipher_version` returns `None` if no connection
+ has been established.
+ """
+ ctx = Context(TLSv1_METHOD)
+ conn = Connection(ctx, None)
+ assert conn.get_cipher_version() is None
+
+ def test_get_cipher_version(self):
+ """
+ `Connection.get_cipher_version` returns a `unicode` string giving
+ the protocol name of the currently used cipher.
+ """
+ server, client = loopback()
+ server_cipher_version, client_cipher_version = \
+ server.get_cipher_version(), client.get_cipher_version()
+
+ assert isinstance(server_cipher_version, text_type)
+ assert isinstance(client_cipher_version, text_type)
+
+ assert server_cipher_version == client_cipher_version
+
+ def test_get_cipher_bits_before_connect(self):
+ """
+ `Connection.get_cipher_bits` returns `None` if no connection has
+ been established.
+ """
+ ctx = Context(TLSv1_METHOD)
+ conn = Connection(ctx, None)
+ assert conn.get_cipher_bits() is None
+
+ def test_get_cipher_bits(self):
+ """
+ `Connection.get_cipher_bits` returns the number of secret bits
+ of the currently used cipher.
+ """
+ server, client = loopback()
+ server_cipher_bits, client_cipher_bits = \
+ server.get_cipher_bits(), client.get_cipher_bits()
+
+ assert isinstance(server_cipher_bits, int)
+ assert isinstance(client_cipher_bits, int)
+
+ assert server_cipher_bits == client_cipher_bits
+
+ def test_get_protocol_version_name(self):
+ """
+ `Connection.get_protocol_version_name()` returns a string giving the
+ protocol version of the current connection.
+ """
+ server, client = loopback()
+ client_protocol_version_name = client.get_protocol_version_name()
+ server_protocol_version_name = server.get_protocol_version_name()
+
+ assert isinstance(server_protocol_version_name, text_type)
+ assert isinstance(client_protocol_version_name, text_type)
+
+ assert server_protocol_version_name == client_protocol_version_name
+
+ def test_get_protocol_version(self):
+ """
+ `Connection.get_protocol_version()` returns an integer
+ giving the protocol version of the current connection.
+ """
+ server, client = loopback()
+ client_protocol_version = client.get_protocol_version()
+ server_protocol_version = server.get_protocol_version()
+
+ assert isinstance(server_protocol_version, int)
+ assert isinstance(client_protocol_version, int)
+
+ assert server_protocol_version == client_protocol_version
+
+ def test_wantReadError(self):
+ """
+ `Connection.bio_read` raises `OpenSSL.SSL.WantReadError` if there are
+ no bytes available to be read from the BIO.
+ """
+ ctx = Context(TLSv1_METHOD)
+ conn = Connection(ctx, None)
+ with pytest.raises(WantReadError):
+ conn.bio_read(1024)
+
+ @pytest.mark.parametrize('bufsize', [1.0, None, object(), 'bufsize'])
+ def test_bio_read_wrong_args(self, bufsize):
+ """
+ `Connection.bio_read` raises `TypeError` if passed a non-integer
+ argument.
+ """
+ ctx = Context(TLSv1_METHOD)
+ conn = Connection(ctx, None)
+ with pytest.raises(TypeError):
+ conn.bio_read(bufsize)
+
+ def test_buffer_size(self):
+ """
+ `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)
+ assert 2 == len(data)
+
+ @skip_if_py3
+ def test_buffer_size_long(self):
+ """
+ On Python 2 `Connection.bio_read` accepts values of type `long` as
+ well as `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))
+ assert 2 == len(data)
+
+
+class TestConnectionGetCipherList(object):
+ """
+ Tests for `Connection.get_cipher_list`.
+ """
+ def test_result(self):
+ """
+ `Connection.get_cipher_list` returns a list of `bytes` giving the
+ names of the ciphers which might be used.
+ """
+ connection = Connection(Context(TLSv1_METHOD), None)
+ ciphers = connection.get_cipher_list()
+ assert isinstance(ciphers, list)
+ for cipher in ciphers:
+ assert isinstance(cipher, str)
+
+
+class VeryLarge(bytes):
+ """
+ Mock object so that we don't have to allocate 2**31 bytes
+ """
+ def __len__(self):
+ return 2**31
+
+
+class TestConnectionSend(object):
+ """
+ Tests for `Connection.send`.
+ """
+ def test_wrong_args(self):
+ """
+ When called with arguments other than string argument for its first
+ parameter, `Connection.send` raises `TypeError`.
+ """
+ connection = Connection(Context(TLSv1_METHOD), None)
+ with pytest.raises(TypeError):
+ connection.send(object())
+
+ def test_short_bytes(self):
+ """
+ When passed a short byte string, `Connection.send` transmits all of it
+ and returns the number of bytes sent.
+ """
+ server, client = loopback()
+ count = server.send(b'xy')
+ assert count == 2
+ assert client.recv(2) == b'xy'
+
+ def test_text(self):
+ """
+ When passed a text, `Connection.send` transmits all of it and
+ returns the number of bytes sent. It also raises a DeprecationWarning.
+ """
+ server, client = loopback()
+ with pytest.warns(DeprecationWarning) as w:
+ simplefilter("always")
+ count = server.send(b"xy".decode("ascii"))
+ assert (
+ "{0} for buf is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ ) == str(w[-1].message))
+ assert count == 2
+ assert client.recv(2) == b'xy'
+
+ def test_short_memoryview(self):
+ """
+ When passed a memoryview onto a small number of bytes,
+ `Connection.send` transmits all of them and returns the number
+ of bytes sent.
+ """
+ server, client = loopback()
+ count = server.send(memoryview(b'xy'))
+ assert count == 2
+ assert client.recv(2) == b'xy'
+
+ @skip_if_py3
+ def test_short_buffer(self):
+ """
+ When passed a buffer containing a small number of bytes,
+ `Connection.send` transmits all of them and returns the number
+ of bytes sent.
+ """
+ server, client = loopback()
+ count = server.send(buffer(b'xy'))
+ assert count == 2
+ assert client.recv(2) == b'xy'
+
+ @pytest.mark.skipif(
+ sys.maxsize < 2**31,
+ reason="sys.maxsize < 2**31 - test requires 64 bit"
+ )
+ def test_buf_too_large(self):
+ """
+ When passed a buffer containing >= 2**31 bytes,
+ `Connection.send` bails out as SSL_write only
+ accepts an int for the buffer length.
+ """
+ connection = Connection(Context(TLSv1_METHOD), None)
+ with pytest.raises(ValueError) as exc_info:
+ connection.send(VeryLarge())
+ exc_info.match(r"Cannot send more than .+ bytes at once")
+
+
+def _make_memoryview(size):
+ """
+ Create a new ``memoryview`` wrapped around a ``bytearray`` of the given
+ size.
+ """
+ return memoryview(bytearray(size))
+
+
+class TestConnectionRecvInto(object):
+ """
+ Tests for `Connection.recv_into`.
+ """
+ def _no_length_test(self, factory):
+ """
+ Assert that when the given buffer is passed to `Connection.recv_into`,
+ whatever bytes are available to be received that fit into that buffer
+ are written into that buffer.
+ """
+ output_buffer = factory(5)
+
+ server, client = loopback()
+ server.send(b'xy')
+
+ assert client.recv_into(output_buffer) == 2
+ assert output_buffer == bytearray(b'xy\x00\x00\x00')
+
+ def test_bytearray_no_length(self):
+ """
+ `Connection.recv_into` can be passed a `bytearray` instance and data
+ in the receive buffer is written to it.
+ """
+ self._no_length_test(bytearray)
+
+ def _respects_length_test(self, factory):
+ """
+ Assert that when the given buffer is passed to `Connection.recv_into`
+ along with a value for `nbytes` that is less than the size of that
+ buffer, only `nbytes` bytes are written into the buffer.
+ """
+ output_buffer = factory(10)
+
+ server, client = loopback()
+ server.send(b'abcdefghij')
+
+ assert client.recv_into(output_buffer, 5) == 5
+ assert output_buffer == bytearray(b'abcde\x00\x00\x00\x00\x00')
+
+ def test_bytearray_respects_length(self):
+ """
+ When called with a `bytearray` instance, `Connection.recv_into`
+ respects the `nbytes` parameter and doesn't copy in more than that
+ number of bytes.
+ """
+ self._respects_length_test(bytearray)
+
+ def _doesnt_overfill_test(self, factory):
+ """
+ Assert that if there are more bytes available to be read from the
+ receive buffer than would fit into the buffer passed to
+ `Connection.recv_into`, only as many as fit are written into it.
+ """
+ output_buffer = factory(5)
+
+ server, client = loopback()
+ server.send(b'abcdefghij')
+
+ assert client.recv_into(output_buffer) == 5
+ assert output_buffer == bytearray(b'abcde')
+ rest = client.recv(5)
+ assert b'fghij' == rest
+
+ def test_bytearray_doesnt_overfill(self):
+ """
+ When called with a `bytearray` instance, `Connection.recv_into`
+ respects the size of the array and doesn't write more bytes into it
+ than will fit.
+ """
+ self._doesnt_overfill_test(bytearray)
+
+ def test_bytearray_really_doesnt_overfill(self):
+ """
+ When called with a `bytearray` instance and an `nbytes` value that is
+ too large, `Connection.recv_into` respects the size of the array and
+ not the `nbytes` value and doesn't write more bytes into the buffer
+ than will fit.
+ """
+ self._doesnt_overfill_test(bytearray)
+
+ def test_peek(self):
+ server, client = loopback()
+ server.send(b'xy')
+
+ for _ in range(2):
+ output_buffer = bytearray(5)
+ assert client.recv_into(output_buffer, flags=MSG_PEEK) == 2
+ assert output_buffer == bytearray(b'xy\x00\x00\x00')
+
+ def test_memoryview_no_length(self):
+ """
+ `Connection.recv_into` can be passed a `memoryview` instance and data
+ in the receive buffer is written to it.
+ """
+ self._no_length_test(_make_memoryview)
+
+ def test_memoryview_respects_length(self):
+ """
+ When called with a `memoryview` instance, `Connection.recv_into`
+ respects the ``nbytes`` parameter and doesn't copy more than that
+ number of bytes in.
+ """
+ self._respects_length_test(_make_memoryview)
+
+ def test_memoryview_doesnt_overfill(self):
+ """
+ When called with a `memoryview` instance, `Connection.recv_into`
+ respects the size of the array and doesn't write more bytes into it
+ than will fit.
+ """
+ self._doesnt_overfill_test(_make_memoryview)
+
+ def test_memoryview_really_doesnt_overfill(self):
+ """
+ When called with a `memoryview` instance and an `nbytes` value that is
+ too large, `Connection.recv_into` respects the size of the array and
+ not the `nbytes` value and doesn't write more bytes into the buffer
+ than will fit.
+ """
+ self._doesnt_overfill_test(_make_memoryview)
+
+
+class TestConnectionSendall(object):
+ """
+ Tests for `Connection.sendall`.
+ """
+ def test_wrong_args(self):
+ """
+ When called with arguments other than a string argument for its first
+ parameter, `Connection.sendall` raises `TypeError`.
+ """
+ connection = Connection(Context(TLSv1_METHOD), None)
+ with pytest.raises(TypeError):
+ connection.sendall(object())
+
+ def test_short(self):
+ """
+ `Connection.sendall` transmits all of the bytes in the string
+ passed to it.
+ """
+ server, client = loopback()
+ server.sendall(b'x')
+ assert client.recv(1) == b'x'
+
+ def test_text(self):
+ """
+ `Connection.sendall` transmits all the content in the string passed
+ to it, raising a DeprecationWarning in case of this being a text.
+ """
+ server, client = loopback()
+ with pytest.warns(DeprecationWarning) as w:
+ simplefilter("always")
+ server.sendall(b"x".decode("ascii"))
+ assert (
+ "{0} for buf is no longer accepted, use bytes".format(
+ WARNING_TYPE_EXPECTED
+ ) == str(w[-1].message))
+ assert client.recv(1) == b"x"
+
+ def test_short_memoryview(self):
+ """
+ When passed a memoryview onto a small number of bytes,
+ `Connection.sendall` transmits all of them.
+ """
+ server, client = loopback()
+ server.sendall(memoryview(b'x'))
+ assert client.recv(1) == b'x'
+
+ @skip_if_py3
+ def test_short_buffers(self):
+ """
+ When passed a buffer containing a small number of bytes,
+ `Connection.sendall` transmits all of them.
+ """
+ server, client = loopback()
+ server.sendall(buffer(b'x'))
+ assert client.recv(1) == b'x'
+
+ def test_long(self):
+ """
+ `Connection.sendall` transmits all the bytes in the string passed to it
+ even if this requires multiple calls of an underlying write function.
+ """
+ server, client = loopback()
+ # Should be enough, underlying SSL_write should only do 16k at a time.
+ # On Windows, after 32k of bytes the write will block (forever
+ # - because no one is yet reading).
+ message = b'x' * (1024 * 32 - 1) + b'y'
+ server.sendall(message)
+ accum = []
+ received = 0
+ while received < len(message):
+ data = client.recv(1024)
+ accum.append(data)
+ received += len(data)
+ assert message == b''.join(accum)
+
+ def test_closed(self):
+ """
+ If the underlying socket is closed, `Connection.sendall` propagates the
+ write error from the low level write call.
+ """
+ server, client = loopback()
+ server.sock_shutdown(2)
+ with pytest.raises(SysCallError) as err:
+ server.sendall(b"hello, world")
+ if platform == "win32":
+ assert err.value.args[0] == ESHUTDOWN
+ else:
+ assert err.value.args[0] == EPIPE
+
+
+class TestConnectionRenegotiate(object):
+ """
+ Tests for SSL renegotiation APIs.
+ """
+ def test_total_renegotiations(self):
+ """
+ `Connection.total_renegotiations` returns `0` before any renegotiations
+ have happened.
+ """
+ connection = Connection(Context(TLSv1_METHOD), None)
+ assert connection.total_renegotiations() == 0
+
+ def test_renegotiate(self):
+ """
+ Go through a complete renegotiation cycle.
+ """
+ server, client = loopback(
+ lambda s: loopback_server_factory(s, TLSv1_2_METHOD),
+ lambda s: loopback_client_factory(s, TLSv1_2_METHOD),
+ )
+
+ server.send(b"hello world")
+
+ assert b"hello world" == client.recv(len(b"hello world"))
+
+ assert 0 == server.total_renegotiations()
+ assert False is server.renegotiate_pending()
+
+ assert True is server.renegotiate()
+
+ assert True is server.renegotiate_pending()
+
+ server.setblocking(False)
+ client.setblocking(False)
+
+ client.do_handshake()
+ server.do_handshake()
+
+ assert 1 == server.total_renegotiations()
+ while False is server.renegotiate_pending():
+ pass
+
+
+class TestError(object):
+ """
+ Unit tests for `OpenSSL.SSL.Error`.
+ """
+ def test_type(self):
+ """
+ `Error` is an exception type.
+ """
+ assert issubclass(Error, Exception)
+ assert Error.__name__ == 'Error'
+
+
+class TestConstants(object):
+ """
+ Tests for the values of constants exposed in `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
+ their values.
+ """
+ @pytest.mark.skipif(
+ OP_NO_QUERY_MTU is None,
+ reason="OP_NO_QUERY_MTU unavailable - OpenSSL version may be too old"
+ )
+ def test_op_no_query_mtu(self):
+ """
+ The value of `OpenSSL.SSL.OP_NO_QUERY_MTU` is 0x1000, the value
+ of `SSL_OP_NO_QUERY_MTU` defined by `openssl/ssl.h`.
+ """
+ assert OP_NO_QUERY_MTU == 0x1000
+
+ @pytest.mark.skipif(
+ OP_COOKIE_EXCHANGE is None,
+ reason="OP_COOKIE_EXCHANGE unavailable - "
+ "OpenSSL version may be too old"
+ )
+ def test_op_cookie_exchange(self):
+ """
+ The value of `OpenSSL.SSL.OP_COOKIE_EXCHANGE` is 0x2000, the
+ value of `SSL_OP_COOKIE_EXCHANGE` defined by `openssl/ssl.h`.
+ """
+ assert OP_COOKIE_EXCHANGE == 0x2000
+
+ @pytest.mark.skipif(
+ OP_NO_TICKET is None,
+ reason="OP_NO_TICKET unavailable - OpenSSL version may be too old"
+ )
+ def test_op_no_ticket(self):
+ """
+ The value of `OpenSSL.SSL.OP_NO_TICKET` is 0x4000, the value of
+ `SSL_OP_NO_TICKET` defined by `openssl/ssl.h`.
+ """
+ assert OP_NO_TICKET == 0x4000
+
+ @pytest.mark.skipif(
+ OP_NO_COMPRESSION is None,
+ reason="OP_NO_COMPRESSION unavailable - OpenSSL version may be too old"
+ )
+ def test_op_no_compression(self):
+ """
+ The value of `OpenSSL.SSL.OP_NO_COMPRESSION` is 0x20000, the
+ value of `SSL_OP_NO_COMPRESSION` defined by `openssl/ssl.h`.
+ """
+ assert OP_NO_COMPRESSION == 0x20000
+
+ def test_sess_cache_off(self):
+ """
+ The value of `OpenSSL.SSL.SESS_CACHE_OFF` 0x0, the value of
+ `SSL_SESS_CACHE_OFF` defined by `openssl/ssl.h`.
+ """
+ assert 0x0 == SESS_CACHE_OFF
+
+ def test_sess_cache_client(self):
+ """
+ The value of `OpenSSL.SSL.SESS_CACHE_CLIENT` 0x1, the value of
+ `SSL_SESS_CACHE_CLIENT` defined by `openssl/ssl.h`.
+ """
+ assert 0x1 == SESS_CACHE_CLIENT
+
+ def test_sess_cache_server(self):
+ """
+ The value of `OpenSSL.SSL.SESS_CACHE_SERVER` 0x2, the value of
+ `SSL_SESS_CACHE_SERVER` defined by `openssl/ssl.h`.
+ """
+ assert 0x2 == SESS_CACHE_SERVER
+
+ def test_sess_cache_both(self):
+ """
+ The value of `OpenSSL.SSL.SESS_CACHE_BOTH` 0x3, the value of
+ `SSL_SESS_CACHE_BOTH` defined by `openssl/ssl.h`.
+ """
+ assert 0x3 == SESS_CACHE_BOTH
+
+ def test_sess_cache_no_auto_clear(self):
+ """
+ The value of `OpenSSL.SSL.SESS_CACHE_NO_AUTO_CLEAR` 0x80, the
+ value of `SSL_SESS_CACHE_NO_AUTO_CLEAR` defined by
+ `openssl/ssl.h`.
+ """
+ assert 0x80 == SESS_CACHE_NO_AUTO_CLEAR
+
+ def test_sess_cache_no_internal_lookup(self):
+ """
+ The value of `OpenSSL.SSL.SESS_CACHE_NO_INTERNAL_LOOKUP` 0x100,
+ the value of `SSL_SESS_CACHE_NO_INTERNAL_LOOKUP` defined by
+ `openssl/ssl.h`.
+ """
+ assert 0x100 == SESS_CACHE_NO_INTERNAL_LOOKUP
+
+ def test_sess_cache_no_internal_store(self):
+ """
+ The value of `OpenSSL.SSL.SESS_CACHE_NO_INTERNAL_STORE` 0x200,
+ the value of `SSL_SESS_CACHE_NO_INTERNAL_STORE` defined by
+ `openssl/ssl.h`.
+ """
+ assert 0x200 == SESS_CACHE_NO_INTERNAL_STORE
+
+ def test_sess_cache_no_internal(self):
+ """
+ The value of `OpenSSL.SSL.SESS_CACHE_NO_INTERNAL` 0x300, the
+ value of `SSL_SESS_CACHE_NO_INTERNAL` defined by
+ `openssl/ssl.h`.
+ """
+ assert 0x300 == SESS_CACHE_NO_INTERNAL
+
+
+class TestMemoryBIO(object):
+ """
+ Tests for `OpenSSL.SSL.Connection` using a memory BIO.
+ """
+ def _server(self, sock):
+ """
+ Create a new server-side SSL `Connection` object wrapped around `sock`.
+ """
+ # Create the server side Connection. This is mostly setup boilerplate
+ # - use TLSv1, use a particular certificate, etc.
+ server_ctx = Context(TLSv1_METHOD)
+ server_ctx.set_options(OP_NO_SSLv2 | OP_NO_SSLv3 | OP_SINGLE_DH_USE)
+ server_ctx.set_verify(
+ VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT | VERIFY_CLIENT_ONCE,
+ verify_cb
+ )
+ server_store = server_ctx.get_cert_store()
+ server_ctx.use_privatekey(
+ load_privatekey(FILETYPE_PEM, server_key_pem))
+ server_ctx.use_certificate(
+ load_certificate(FILETYPE_PEM, server_cert_pem))
+ server_ctx.check_privatekey()
+ server_store.add_cert(load_certificate(FILETYPE_PEM, root_cert_pem))
+ # Here the Connection is actually created. If None is passed as the
+ # 2nd parameter, it indicates a memory BIO should be created.
+ server_conn = Connection(server_ctx, sock)
+ server_conn.set_accept_state()
+ return server_conn
+
+ def _client(self, sock):
+ """
+ Create a new client-side SSL `Connection` object wrapped around `sock`.
+ """
+ # Now create the client side Connection. Similar boilerplate to the
+ # above.
+ client_ctx = Context(TLSv1_METHOD)
+ client_ctx.set_options(OP_NO_SSLv2 | OP_NO_SSLv3 | OP_SINGLE_DH_USE)
+ client_ctx.set_verify(
+ VERIFY_PEER | VERIFY_FAIL_IF_NO_PEER_CERT | VERIFY_CLIENT_ONCE,
+ verify_cb
+ )
+ client_store = client_ctx.get_cert_store()
+ client_ctx.use_privatekey(
+ load_privatekey(FILETYPE_PEM, client_key_pem))
+ client_ctx.use_certificate(
+ load_certificate(FILETYPE_PEM, client_cert_pem))
+ client_ctx.check_privatekey()
+ client_store.add_cert(load_certificate(FILETYPE_PEM, root_cert_pem))
+ client_conn = Connection(client_ctx, sock)
+ client_conn.set_connect_state()
+ return client_conn
+
+ def test_memory_connect(self):
+ """
+ Two `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.
+ """
+ server_conn = self._server(None)
+ client_conn = self._client(None)
+
+ # There should be no key or nonces yet.
+ assert server_conn.master_key() is None
+ assert server_conn.client_random() is None
+ assert server_conn.server_random() is None
+
+ # First, the handshake needs to happen. We'll deliver bytes back and
+ # forth between the client and server until neither of them feels like
+ # speaking any more.
+ assert interact_in_memory(client_conn, server_conn) is None
+
+ # Now that the handshake is done, there should be a key and nonces.
+ assert server_conn.master_key() is not None
+ assert server_conn.client_random() is not None
+ assert server_conn.server_random() is not None
+ assert server_conn.client_random() == client_conn.client_random()
+ assert server_conn.server_random() == client_conn.server_random()
+ assert server_conn.client_random() != server_conn.server_random()
+ assert client_conn.client_random() != client_conn.server_random()
+
+ # Export key material for other uses.
+ cekm = client_conn.export_keying_material(b'LABEL', 32)
+ sekm = server_conn.export_keying_material(b'LABEL', 32)
+ assert cekm is not None
+ assert sekm is not None
+ assert cekm == sekm
+ assert len(sekm) == 32
+
+ # Export key material for other uses with additional context.
+ cekmc = client_conn.export_keying_material(b'LABEL', 32, b'CONTEXT')
+ sekmc = server_conn.export_keying_material(b'LABEL', 32, b'CONTEXT')
+ assert cekmc is not None
+ assert sekmc is not None
+ assert cekmc == sekmc
+ assert cekmc != cekm
+ assert sekmc != sekm
+ # Export with alternate label
+ cekmt = client_conn.export_keying_material(b'test', 32, b'CONTEXT')
+ sekmt = server_conn.export_keying_material(b'test', 32, b'CONTEXT')
+ assert cekmc != cekmt
+ assert sekmc != sekmt
+
+ # Here are the bytes we'll try to send.
+ important_message = b'One if by land, two if by sea.'
+
+ server_conn.write(important_message)
+ assert (
+ interact_in_memory(client_conn, server_conn) ==
+ (client_conn, important_message))
+
+ client_conn.write(important_message[::-1])
+ assert (
+ interact_in_memory(client_conn, server_conn) ==
+ (server_conn, important_message[::-1]))
+
+ def test_socket_connect(self):
+ """
+ Just like `test_memory_connect` 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 `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.
+ """
+ server_conn, client_conn = loopback()
+
+ important_message = b"Help me Obi Wan Kenobi, you're my only hope."
+ client_conn.send(important_message)
+ msg = server_conn.recv(1024)
+ assert msg == important_message
+
+ # Again in the other direction, just for fun.
+ important_message = important_message[::-1]
+ server_conn.send(important_message)
+ msg = client_conn.recv(1024)
+ assert msg == important_message
+
+ def test_socket_overrides_memory(self):
+ """
+ Test that `OpenSSL.SSL.bio_read` and `OpenSSL.SSL.bio_write` don't
+ work on `OpenSSL.SSL.Connection`() that use sockets.
+ """
+ context = Context(TLSv1_METHOD)
+ client = socket()
+ clientSSL = Connection(context, client)
+ with pytest.raises(TypeError):
+ clientSSL.bio_read(100)
+ with pytest.raises(TypeError):
+ clientSSL.bio_write("foo")
+ with pytest.raises(TypeError):
+ clientSSL.bio_shutdown()
+
+ def test_outgoing_overflow(self):
+ """
+ If more bytes than can be written to the memory BIO are passed to
+ `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.
+ """
+ server = self._server(None)
+ client = self._client(None)
+
+ interact_in_memory(client, server)
+
+ size = 2 ** 15
+ 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.
+ assert sent < size
+
+ receiver, received = interact_in_memory(client, server)
+ assert receiver is server
+
+ # We can rely on all of these bytes being received at once because
+ # loopback passes 2 ** 16 to recv - more than 2 ** 15.
+ assert len(received) == sent
+
+ def test_shutdown(self):
+ """
+ `Connection.bio_shutdown` signals the end of the data stream
+ from which the `Connection` reads.
+ """
+ server = self._server(None)
+ server.bio_shutdown()
+ with pytest.raises(Error) as err:
+ server.recv(1024)
+ # We don't want WantReadError or ZeroReturnError or anything - it's a
+ # handshake failure.
+ assert type(err.value) in [Error, SysCallError]
+
+ def test_unexpected_EOF(self):
+ """
+ If the connection is lost before an orderly SSL shutdown occurs,
+ `OpenSSL.SSL.SysCallError` is raised with a message of
+ "Unexpected EOF".
+ """
+ server_conn, client_conn = loopback()
+ client_conn.sock_shutdown(SHUT_RDWR)
+ with pytest.raises(SysCallError) as err:
+ server_conn.recv(1024)
+ assert err.value.args == (-1, "Unexpected EOF")
+
+ def _check_client_ca_list(self, func):
+ """
+ Verify the return value of the `get_client_ca_list` method for
+ server and client connections.
+
+ :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 `get_client_ca_list` returns the proper value at
+ various times.
+ """
+ server = self._server(None)
+ client = self._client(None)
+ assert client.get_client_ca_list() == []
+ assert server.get_client_ca_list() == []
+ ctx = server.get_context()
+ expected = func(ctx)
+ assert client.get_client_ca_list() == []
+ assert server.get_client_ca_list() == expected
+ interact_in_memory(client, server)
+ assert client.get_client_ca_list() == expected
+ assert server.get_client_ca_list() == expected
+
+ def test_set_client_ca_list_errors(self):
+ """
+ `Context.set_client_ca_list` raises a `TypeError` if called with a
+ non-list or a list that contains objects other than X509Names.
+ """
+ ctx = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ ctx.set_client_ca_list("spam")
+ with pytest.raises(TypeError):
+ ctx.set_client_ca_list(["spam"])
+
+ def test_set_empty_ca_list(self):
+ """
+ If passed an empty list, `Context.set_client_ca_list` configures the
+ context to send no CA names to the client and, on both the server and
+ client sides, `Connection.get_client_ca_list` returns an empty list
+ after the connection is set up.
+ """
+ def no_ca(ctx):
+ ctx.set_client_ca_list([])
+ return []
+ self._check_client_ca_list(no_ca)
+
+ def test_set_one_ca_list(self):
+ """
+ If passed a list containing a single X509Name,
+ `Context.set_client_ca_list` configures the context to send
+ that CA name to the client and, on both the server and client sides,
+ `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)
+ cadesc = cacert.get_subject()
+
+ def single_ca(ctx):
+ ctx.set_client_ca_list([cadesc])
+ return [cadesc]
+ self._check_client_ca_list(single_ca)
+
+ def test_set_multiple_ca_list(self):
+ """
+ If passed a list containing multiple X509Name objects,
+ `Context.set_client_ca_list` configures the context to send
+ those CA names to the client and, on both the server and client sides,
+ `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)
+ clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
+
+ sedesc = secert.get_subject()
+ cldesc = clcert.get_subject()
+
+ def multiple_ca(ctx):
+ L = [sedesc, cldesc]
+ ctx.set_client_ca_list(L)
+ return L
+ self._check_client_ca_list(multiple_ca)
+
+ def test_reset_ca_list(self):
+ """
+ If called multiple times, only the X509Names passed to the final call
+ of `Context.set_client_ca_list` are used to configure the CA
+ names sent to the client.
+ """
+ cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ secert = load_certificate(FILETYPE_PEM, server_cert_pem)
+ clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
+
+ cadesc = cacert.get_subject()
+ sedesc = secert.get_subject()
+ cldesc = clcert.get_subject()
+
+ def changed_ca(ctx):
+ ctx.set_client_ca_list([sedesc, cldesc])
+ ctx.set_client_ca_list([cadesc])
+ return [cadesc]
+ self._check_client_ca_list(changed_ca)
+
+ def test_mutated_ca_list(self):
+ """
+ If the list passed to `Context.set_client_ca_list` is mutated
+ afterwards, this does not affect the list of CA names sent to the
+ client.
+ """
+ cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ secert = load_certificate(FILETYPE_PEM, server_cert_pem)
+
+ cadesc = cacert.get_subject()
+ sedesc = secert.get_subject()
+
+ def mutated_ca(ctx):
+ L = [cadesc]
+ ctx.set_client_ca_list([cadesc])
+ L.append(sedesc)
+ return [cadesc]
+ self._check_client_ca_list(mutated_ca)
+
+ def test_add_client_ca_wrong_args(self):
+ """
+ `Context.add_client_ca` raises `TypeError` if called with
+ a non-X509 object.
+ """
+ ctx = Context(TLSv1_METHOD)
+ with pytest.raises(TypeError):
+ ctx.add_client_ca("spam")
+
+ def test_one_add_client_ca(self):
+ """
+ A certificate's subject can be added as a CA to be sent to the client
+ with `Context.add_client_ca`.
+ """
+ cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ cadesc = cacert.get_subject()
+
+ def single_ca(ctx):
+ ctx.add_client_ca(cacert)
+ return [cadesc]
+ self._check_client_ca_list(single_ca)
+
+ def test_multiple_add_client_ca(self):
+ """
+ Multiple CA names can be sent to the client by calling
+ `Context.add_client_ca` with multiple X509 objects.
+ """
+ cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ secert = load_certificate(FILETYPE_PEM, server_cert_pem)
+
+ cadesc = cacert.get_subject()
+ sedesc = secert.get_subject()
+
+ def multiple_ca(ctx):
+ ctx.add_client_ca(cacert)
+ ctx.add_client_ca(secert)
+ return [cadesc, sedesc]
+ self._check_client_ca_list(multiple_ca)
+
+ def test_set_and_add_client_ca(self):
+ """
+ A call to `Context.set_client_ca_list` followed by a call to
+ `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)
+ secert = load_certificate(FILETYPE_PEM, server_cert_pem)
+ clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
+
+ cadesc = cacert.get_subject()
+ sedesc = secert.get_subject()
+ cldesc = clcert.get_subject()
+
+ def mixed_set_add_ca(ctx):
+ ctx.set_client_ca_list([cadesc, sedesc])
+ ctx.add_client_ca(clcert)
+ return [cadesc, sedesc, cldesc]
+ self._check_client_ca_list(mixed_set_add_ca)
+
+ def test_set_after_add_client_ca(self):
+ """
+ A call to `Context.set_client_ca_list` after a call to
+ `Context.add_client_ca` replaces the CA name specified by the
+ former call with the names specified by the latter call.
+ """
+ cacert = load_certificate(FILETYPE_PEM, root_cert_pem)
+ secert = load_certificate(FILETYPE_PEM, server_cert_pem)
+ clcert = load_certificate(FILETYPE_PEM, server_cert_pem)
+
+ cadesc = cacert.get_subject()
+ sedesc = secert.get_subject()
+
+ def set_replaces_add_ca(ctx):
+ ctx.add_client_ca(clcert)
+ ctx.set_client_ca_list([cadesc])
+ ctx.add_client_ca(secert)
+ return [cadesc, sedesc]
+ self._check_client_ca_list(set_replaces_add_ca)
+
+
+class TestInfoConstants(object):
+ """
+ Tests for assorted constants exposed for use in info callbacks.
+ """
+ def test_integers(self):
+ """
+ All of the info constants are integers.
+
+ This is a very weak test. It would be nice to have one that actually
+ verifies that as certain info events happen, the value passed to the
+ info callback matches up with the constant exposed by OpenSSL.SSL.
+ """
+ for const in [
+ SSL_ST_CONNECT, SSL_ST_ACCEPT, SSL_ST_MASK,
+ SSL_CB_LOOP, SSL_CB_EXIT, SSL_CB_READ, SSL_CB_WRITE, SSL_CB_ALERT,
+ SSL_CB_READ_ALERT, SSL_CB_WRITE_ALERT, SSL_CB_ACCEPT_LOOP,
+ SSL_CB_ACCEPT_EXIT, SSL_CB_CONNECT_LOOP, SSL_CB_CONNECT_EXIT,
+ SSL_CB_HANDSHAKE_START, SSL_CB_HANDSHAKE_DONE
+ ]:
+ assert isinstance(const, int)
+
+ # These constants don't exist on OpenSSL 1.1.0
+ for const in [
+ SSL_ST_INIT, SSL_ST_BEFORE, SSL_ST_OK, SSL_ST_RENEGOTIATE
+ ]:
+ assert const is None or isinstance(const, int)
+
+
+class TestRequires(object):
+ """
+ Tests for the decorator factory used to conditionally raise
+ NotImplementedError when older OpenSSLs are used.
+ """
+ def test_available(self):
+ """
+ When the OpenSSL functionality is available the decorated functions
+ work appropriately.
+ """
+ feature_guard = _make_requires(True, "Error text")
+ results = []
+
+ @feature_guard
+ def inner():
+ results.append(True)
+ return True
+
+ assert inner() is True
+ assert [True] == results
+
+ def test_unavailable(self):
+ """
+ When the OpenSSL functionality is not available the decorated function
+ does not execute and NotImplementedError is raised.
+ """
+ feature_guard = _make_requires(False, "Error text")
+
+ @feature_guard
+ def inner(): # pragma: nocover
+ pytest.fail("Should not be called")
+
+ with pytest.raises(NotImplementedError) as e:
+ inner()
+
+ assert "Error text" in str(e.value)
+
+
+class TestOCSP(object):
+ """
+ Tests for PyOpenSSL's OCSP stapling support.
+ """
+ sample_ocsp_data = b"this is totally ocsp data"
+
+ def _client_connection(self, callback, data, request_ocsp=True):
+ """
+ Builds a client connection suitable for using OCSP.
+
+ :param callback: The callback to register for OCSP.
+ :param data: The opaque data object that will be handed to the
+ OCSP callback.
+ :param request_ocsp: Whether the client will actually ask for OCSP
+ stapling. Useful for testing only.
+ """
+ ctx = Context(SSLv23_METHOD)
+ ctx.set_ocsp_client_callback(callback, data)
+ client = Connection(ctx)
+
+ if request_ocsp:
+ client.request_ocsp()
+
+ client.set_connect_state()
+ return client
+
+ def _server_connection(self, callback, data):
+ """
+ Builds a server connection suitable for using OCSP.
+
+ :param callback: The callback to register for OCSP.
+ :param data: The opaque data object that will be handed to the
+ OCSP callback.
+ """
+ ctx = Context(SSLv23_METHOD)
+ ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
+ ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
+ ctx.set_ocsp_server_callback(callback, data)
+ server = Connection(ctx)
+ server.set_accept_state()
+ return server
+
+ def test_callbacks_arent_called_by_default(self):
+ """
+ If both the client and the server have registered OCSP callbacks, but
+ the client does not send the OCSP request, neither callback gets
+ called.
+ """
+ def ocsp_callback(*args, **kwargs): # pragma: nocover
+ pytest.fail("Should not be called")
+
+ client = self._client_connection(
+ callback=ocsp_callback, data=None, request_ocsp=False
+ )
+ server = self._server_connection(callback=ocsp_callback, data=None)
+ handshake_in_memory(client, server)
+
+ def test_client_negotiates_without_server(self):
+ """
+ If the client wants to do OCSP but the server does not, the handshake
+ succeeds, and the client callback fires with an empty byte string.
+ """
+ called = []
+
+ def ocsp_callback(conn, ocsp_data, ignored):
+ called.append(ocsp_data)
+ return True
+
+ client = self._client_connection(callback=ocsp_callback, data=None)
+ server = loopback_server_factory(socket=None)
+ handshake_in_memory(client, server)
+
+ assert len(called) == 1
+ assert called[0] == b''
+
+ def test_client_receives_servers_data(self):
+ """
+ The data the server sends in its callback is received by the client.
+ """
+ calls = []
+
+ def server_callback(*args, **kwargs):
+ return self.sample_ocsp_data
+
+ def client_callback(conn, ocsp_data, ignored):
+ calls.append(ocsp_data)
+ return True
+
+ client = self._client_connection(callback=client_callback, data=None)
+ server = self._server_connection(callback=server_callback, data=None)
+ handshake_in_memory(client, server)
+
+ assert len(calls) == 1
+ assert calls[0] == self.sample_ocsp_data
+
+ def test_callbacks_are_invoked_with_connections(self):
+ """
+ The first arguments to both callbacks are their respective connections.
+ """
+ client_calls = []
+ server_calls = []
+
+ def client_callback(conn, *args, **kwargs):
+ client_calls.append(conn)
+ return True
+
+ def server_callback(conn, *args, **kwargs):
+ server_calls.append(conn)
+ return self.sample_ocsp_data
+
+ client = self._client_connection(callback=client_callback, data=None)
+ server = self._server_connection(callback=server_callback, data=None)
+ handshake_in_memory(client, server)
+
+ assert len(client_calls) == 1
+ assert len(server_calls) == 1
+ assert client_calls[0] is client
+ assert server_calls[0] is server
+
+ def test_opaque_data_is_passed_through(self):
+ """
+ Both callbacks receive an opaque, user-provided piece of data in their
+ callbacks as the final argument.
+ """
+ calls = []
+
+ def server_callback(*args):
+ calls.append(args)
+ return self.sample_ocsp_data
+
+ def client_callback(*args):
+ calls.append(args)
+ return True
+
+ sentinel = object()
+
+ client = self._client_connection(
+ callback=client_callback, data=sentinel
+ )
+ server = self._server_connection(
+ callback=server_callback, data=sentinel
+ )
+ handshake_in_memory(client, server)
+
+ assert len(calls) == 2
+ assert calls[0][-1] is sentinel
+ assert calls[1][-1] is sentinel
+
+ def test_server_returns_empty_string(self):
+ """
+ If the server returns an empty bytestring from its callback, the
+ client callback is called with the empty bytestring.
+ """
+ client_calls = []
+
+ def server_callback(*args):
+ return b''
+
+ def client_callback(conn, ocsp_data, ignored):
+ client_calls.append(ocsp_data)
+ return True
+
+ client = self._client_connection(callback=client_callback, data=None)
+ server = self._server_connection(callback=server_callback, data=None)
+ handshake_in_memory(client, server)
+
+ assert len(client_calls) == 1
+ assert client_calls[0] == b''
+
+ def test_client_returns_false_terminates_handshake(self):
+ """
+ If the client returns False from its callback, the handshake fails.
+ """
+ def server_callback(*args):
+ return self.sample_ocsp_data
+
+ def client_callback(*args):
+ return False
+
+ client = self._client_connection(callback=client_callback, data=None)
+ server = self._server_connection(callback=server_callback, data=None)
+
+ with pytest.raises(Error):
+ handshake_in_memory(client, server)
+
+ def test_exceptions_in_client_bubble_up(self):
+ """
+ The callbacks thrown in the client callback bubble up to the caller.
+ """
+ class SentinelException(Exception):
+ pass
+
+ def server_callback(*args):
+ return self.sample_ocsp_data
+
+ def client_callback(*args):
+ raise SentinelException()
+
+ client = self._client_connection(callback=client_callback, data=None)
+ server = self._server_connection(callback=server_callback, data=None)
+
+ with pytest.raises(SentinelException):
+ handshake_in_memory(client, server)
+
+ def test_exceptions_in_server_bubble_up(self):
+ """
+ The callbacks thrown in the server callback bubble up to the caller.
+ """
+ class SentinelException(Exception):
+ pass
+
+ def server_callback(*args):
+ raise SentinelException()
+
+ def client_callback(*args): # pragma: nocover
+ pytest.fail("Should not be called")
+
+ client = self._client_connection(callback=client_callback, data=None)
+ server = self._server_connection(callback=server_callback, data=None)
+
+ with pytest.raises(SentinelException):
+ handshake_in_memory(client, server)
+
+ def test_server_must_return_bytes(self):
+ """
+ The server callback must return a bytestring, or a TypeError is thrown.
+ """
+ def server_callback(*args):
+ return self.sample_ocsp_data.decode('ascii')
+
+ def client_callback(*args): # pragma: nocover
+ pytest.fail("Should not be called")
+
+ client = self._client_connection(callback=client_callback, data=None)
+ server = self._server_connection(callback=server_callback, data=None)
+
+ with pytest.raises(TypeError):
+ handshake_in_memory(client, server)
--- /dev/null
+# Copyright (C) Jean-Paul Calderone
+# See LICENSE for details.
+
+"""
+Unit tests for `OpenSSL.tsafe`.
+"""
+
+from OpenSSL.SSL import TLSv1_METHOD, Context
+from OpenSSL.tsafe import Connection
+
+
+class TestConnection(object):
+ """
+ Tests for `OpenSSL.tsafe.Connection`.
+ """
+ def test_instantiation(self):
+ """
+ `OpenSSL.tsafe.Connection` can be instantiated.
+ """
+ # The following line should not throw an error. This isn't an ideal
+ # test. It would be great to refactor the other Connection tests so
+ # they could automatically be applied to this class too.
+ Connection(Context(TLSv1_METHOD), None)
--- /dev/null
+import pytest
+
+from OpenSSL._util import exception_from_error_queue, lib
+
+
+class TestErrors(object):
+ """
+ Tests for handling of certain OpenSSL error cases.
+ """
+ def test_exception_from_error_queue_nonexistent_reason(self):
+ """
+ :func:`exception_from_error_queue` raises ``ValueError`` when it
+ encounters an OpenSSL error code which does not have a reason string.
+ """
+ lib.ERR_put_error(lib.ERR_LIB_EVP, 0, 1112, b"", 10)
+ with pytest.raises(ValueError) as exc:
+ exception_from_error_queue(ValueError)
+ assert exc.value.args[0][0][2] == ""
--- /dev/null
+# Copyright (C) Jean-Paul Calderone
+# Copyright (C) Twisted Matrix Laboratories.
+# See LICENSE for details.
+"""
+Helpers for the OpenSSL test suite, largely copied from
+U{Twisted<http://twistedmatrix.com/>}.
+"""
+
+from six import PY3
+
+
+# This is the UTF-8 encoding of the SNOWMAN unicode code point.
+NON_ASCII = b"\xe2\x98\x83".decode("utf-8")
+
+
+def is_consistent_type(theType, name, *constructionArgs):
+ """
+ Perform various assertions about *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
+ *theType* to create an instance of it.
+ """
+ assert theType.__name__ == name
+ assert isinstance(theType, type)
+ instance = theType(*constructionArgs)
+ assert type(instance) is theType
+ return True
+
+
+class EqualityTestsMixin(object):
+ """
+ A mixin defining tests for the standard implementation of C{==} and C{!=}.
+ """
+
+ def anInstance(self):
+ """
+ Return an instance of the class under test. Each call to this method
+ must return a different object. All objects returned must be equal to
+ each other.
+ """
+ raise NotImplementedError()
+
+ def anotherInstance(self):
+ """
+ Return an instance of the class under test. Each call to this method
+ must return a different object. The objects must not be equal to the
+ objects returned by C{anInstance}. They may or may not be equal to
+ each other (they will not be compared against each other).
+ """
+ raise NotImplementedError()
+
+ def test_identicalEq(self):
+ """
+ An object compares equal to itself using the C{==} operator.
+ """
+ o = self.anInstance()
+ assert (o == o)
+
+ def test_identicalNe(self):
+ """
+ An object doesn't compare not equal to itself using the C{!=} operator.
+ """
+ o = self.anInstance()
+ assert not (o != o)
+
+ def test_sameEq(self):
+ """
+ Two objects that are equal to each other compare equal to each other
+ using the C{==} operator.
+ """
+ a = self.anInstance()
+ b = self.anInstance()
+ assert (a == b)
+
+ def test_sameNe(self):
+ """
+ Two objects that are equal to each other do not compare not equal to
+ each other using the C{!=} operator.
+ """
+ a = self.anInstance()
+ b = self.anInstance()
+ assert not (a != b)
+
+ def test_differentEq(self):
+ """
+ Two objects that are not equal to each other do not compare equal to
+ each other using the C{==} operator.
+ """
+ a = self.anInstance()
+ b = self.anotherInstance()
+ assert not (a == b)
+
+ def test_differentNe(self):
+ """
+ Two objects that are not equal to each other compare not equal to each
+ other using the C{!=} operator.
+ """
+ a = self.anInstance()
+ b = self.anotherInstance()
+ assert (a != b)
+
+ def test_anotherTypeEq(self):
+ """
+ The object does not compare equal to an object of an unrelated type
+ (which does not implement the comparison) using the C{==} operator.
+ """
+ a = self.anInstance()
+ b = object()
+ assert not (a == b)
+
+ def test_anotherTypeNe(self):
+ """
+ The object compares not equal to an object of an unrelated type (which
+ does not implement the comparison) using the C{!=} operator.
+ """
+ a = self.anInstance()
+ b = object()
+ assert (a != b)
+
+ def test_delegatedEq(self):
+ """
+ The result of comparison using C{==} is delegated to the right-hand
+ operand if it is of an unrelated type.
+ """
+ class Delegate(object):
+ def __eq__(self, other):
+ # Do something crazy and obvious.
+ return [self]
+
+ a = self.anInstance()
+ b = Delegate()
+ assert (a == b) == [b]
+
+ def test_delegateNe(self):
+ """
+ The result of comparison using C{!=} is delegated to the right-hand
+ operand if it is of an unrelated type.
+ """
+ class Delegate(object):
+ def __ne__(self, other):
+ # Do something crazy and obvious.
+ return [self]
+
+ a = self.anInstance()
+ b = Delegate()
+ assert (a != b) == [b]
+
+
+# The type name expected in warnings about using the wrong string type.
+if PY3:
+ WARNING_TYPE_EXPECTED = "str"
+else:
+ WARNING_TYPE_EXPECTED = "unicode"
--- /dev/null
+[tox]
+envlist = {pypy,pypy3,py27,py34,py35,py36,py37}{,-cryptographyMaster,-cryptographyMinimum},py27-twistedMaster,pypi-readme,check-manifest,flake8,docs,coverage-report
+
+[testenv]
+whitelist_externals =
+ openssl
+passenv = ARCHFLAGS CFLAGS LC_ALL LDFLAGS PATH LD_LIBRARY_PATH TERM
+extras =
+ test
+deps =
+ coverage>=4.2
+ cryptographyMaster: git+https://github.com/pyca/cryptography.git
+ cryptographyMinimum: cryptography==2.3.0
+setenv =
+ # Do not allow the executing environment to pollute the test environment
+ # with extra packages.
+ PYTHONPATH=
+ PIP_NO_BINARY=cryptography
+commands =
+ openssl version
+ coverage run --parallel -m OpenSSL.debug
+ coverage run --parallel -m pytest -v {posargs}
+
+[testenv:py27-twistedMaster]
+deps =
+ # [tls,conch] syntax doesn't work here so we enumerate all dependencies.
+ git+https://github.com/twisted/twisted
+ idna
+ service_identity
+ bcrypt
+passenv = ARCHFLAGS CFLAGS LC_ALL LDFLAGS PATH LD_LIBRARY_PATH TERM
+commands =
+ python -c "import OpenSSL.SSL; print(OpenSSL.SSL.SSLeay_version(OpenSSL.SSL.SSLEAY_VERSION))"
+ python -c "import cryptography; print(cryptography.__version__)"
+ python -m twisted.trial --reporter=text twisted
+
+[testenv:py35-urllib3Master]
+basepython=python3.5
+deps =
+ pyasn1
+ ndg-httpsclient
+passenv = ARCHFLAGS CFLAGS LC_ALL LDFLAGS PATH LD_LIBRARY_PATH TERM TRAVIS_INFRA
+whitelist_externals =
+ rm
+commands =
+ python -c "import OpenSSL.SSL; print(OpenSSL.SSL.SSLeay_version(OpenSSL.SSL.SSLEAY_VERSION))"
+ python -c "import cryptography; print(cryptography.__version__)"
+ {toxinidir}/.travis/install_urllib3.sh
+ pytest urllib3/test
+ rm -rf ./urllib3
+
+[testenv:flake8]
+deps =
+ flake8
+skip_install = true
+commands =
+ flake8 src tests examples setup.py
+
+[testenv:pypi-readme]
+deps =
+ readme_renderer
+skip_install = true
+commands =
+ python setup.py check -r -s
+
+[testenv:check-manifest]
+deps =
+ check-manifest
+skip_install = true
+commands =
+ check-manifest
+
+[testenv:docs]
+extras =
+ docs
+basepython = python2.7
+commands =
+ sphinx-build -W -b html doc doc/_build/html
+
+[testenv:coverage-report]
+deps = coverage>=4.2
+skip_install = true
+commands =
+ coverage combine
+ coverage report