diff options
author | Laurens Van Houtven <_@lvh.io> | 2015-05-13 08:06:04 -0700 |
---|---|---|
committer | Laurens Van Houtven <_@lvh.io> | 2015-05-13 08:06:04 -0700 |
commit | e862023f212cfc59f564d02651c2e7280c991019 (patch) | |
tree | 4a06ce7262f98753e096f6c5b11fbc72b4cdd4e1 | |
parent | f7e552d0224e7bd904923d1b32a93356db105102 (diff) | |
parent | e9ae673b3999344a5aaa121bfe6925d172b61c13 (diff) | |
download | pyopenssl-e862023f212cfc59f564d02651c2e7280c991019.tar.gz |
Merge branch 'master' into test-metadata
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | .travis.yml | 43 | ||||
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | MANIFEST.in | 5 | ||||
-rw-r--r-- | OpenSSL/__init__.py | 13 | ||||
-rw-r--r-- | OpenSSL/crypto.py | 607 | ||||
-rw-r--r-- | OpenSSL/version.py | 15 | ||||
-rw-r--r-- | README.rst | 14 | ||||
-rw-r--r-- | TODO | 8 | ||||
-rw-r--r-- | doc/api/crypto.rst | 718 | ||||
-rw-r--r-- | doc/conf.py | 39 | ||||
-rw-r--r-- | examples/certgen.py | 12 | ||||
-rw-r--r-- | examples/mk_simple_certs.py | 23 | ||||
-rw-r--r-- | examples/simple/client.py | 13 | ||||
-rw-r--r-- | examples/simple/server.py | 29 | ||||
-rwxr-xr-x | setup.py | 127 | ||||
-rw-r--r-- | tox.ini | 15 |
17 files changed, 758 insertions, 932 deletions
@@ -8,3 +8,5 @@ __pycache__ doc/_build/ .coverage .eggs +examples/simple/*.cert +examples/simple/*.pkey diff --git a/.travis.yml b/.travis.yml index c244622..0354895 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,33 +10,24 @@ matrix: env: TOXENV=py26 - python: "2.7" env: TOXENV=py27 - - python: "3.2" - env: TOXENV=py32 - python: "3.3" env: TOXENV=py33 - python: "3.4" env: TOXENV=py34 - python: "pypy" env: TOXENV=pypy + # Also run the tests against cryptography master. - python: "2.6" - env: - CRYPTOGRAPHY_GIT_MASTER=true TOXENV=py26 + env: TOXENV=py26-cryptographyMaster - python: "2.7" - env: - CRYPTOGRAPHY_GIT_MASTER=true TOXENV=py27 - - python: "3.2" - env: - CRYPTOGRAPHY_GIT_MASTER=true TOXENV=py32 + env: TOXENV=py27-cryptographyMaster - python: "3.3" - env: - CRYPTOGRAPHY_GIT_MASTER=true TOXENV=py33 + env: TOXENV=py33-cryptographyMaster - python: "3.4" - env: - CRYPTOGRAPHY_GIT_MASTER=true TOXENV=py34 + env: TOXENV=py34-cryptographyMaster - python: "pypy" - env: - CRYPTOGRAPHY_GIT_MASTER=true TOXENV=pypy + env: TOXENV=pypy-cryptographyMaster # Also run at least a little bit against an older version of OpenSSL. - python: "2.7" @@ -49,6 +40,14 @@ matrix: packages: - libssl-dev/lucid + # Meta + - python: "2.7" + env: TOXENV=check-manifest + + - python: "2.7" + env: TOXENV=pypi-readme + + # Let the cryptography master builds fail because they might be triggered by # cryptography changes beyond our control. # Also allow OS X and 0.9.8 to fail at the moment while we fix these new @@ -57,17 +56,13 @@ matrix: - language: generic os: osx env: TOXENV=py27 - - env: CRYPTOGRAPHY_GIT_MASTER=true TOXENV=py26 - - env: CRYPTOGRAPHY_GIT_MASTER=true TOXENV=py27 - - env: CRYPTOGRAPHY_GIT_MASTER=true TOXENV=py32 - - env: CRYPTOGRAPHY_GIT_MASTER=true TOXENV=py33 - - env: CRYPTOGRAPHY_GIT_MASTER=true TOXENV=py34 - - env: CRYPTOGRAPHY_GIT_MASTER=true TOXENV=pypy + - env: TOXENV=py26-cryptographyMaster + - env: TOXENV=py27-cryptographyMaster + - env: TOXENV=py33-cryptographyMaster + - env: TOXENV=py34-cryptographyMaster + - env: TOXENV=pypy-cryptographyMaster - env: OPENSSL=0.9.8 TOXENV=py27 -before_install: - - if [ -n "$CRYPTOGRAPHY_GIT_MASTER" ]; then pip install git+https://github.com/pyca/cryptography.git;fi - install: - | if [[ "$(uname -s)" == 'Darwin' ]]; then @@ -1,3 +1,10 @@ +2015-05-02 Jim Shaver <dcypherd@gmail.com> + + * .travis.yml, setup.py, tox.ini: Removed support for Python 3.2. + This version is rarely used and is now deprecated by a major + dependency of pyOpenSSL (cryptography). Affected users should upgrade + to Python 3.3+. + 2015-04-15 Paul Kehrer <paul.l.kehrer@gmail.com> * OpenSSL/crypto.py, OpenSSL/test/test_crypto.py: Switch to utf8string diff --git a/MANIFEST.in b/MANIFEST.in index 455af6d..7b6f3e7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,8 +1,7 @@ -include LICENSE ChangeLog TODO MANIFEST.in OpenSSL/RATIONALE *.rst tox.ini memdbg.py runtests.py OpenSSL/test/README -exclude leakcheck +include LICENSE ChangeLog MANIFEST.in *.rst tox.ini memdbg.py runtests.py OpenSSL/test/README +exclude leakcheck recursive-include doc * recursive-include examples * recursive-include rpm * recursive-exclude leakcheck *.py *.pem -global-exclude *.pyc prune doc/_build diff --git a/OpenSSL/__init__.py b/OpenSSL/__init__.py index db96e1f..fde6fa7 100644 --- a/OpenSSL/__init__.py +++ b/OpenSSL/__init__.py @@ -6,7 +6,16 @@ pyOpenSSL - A simple wrapper around the OpenSSL library """ from OpenSSL import rand, crypto, SSL -from OpenSSL.version import __version__ +from OpenSSL.version import ( + __author__, __copyright__, __email__, __license__, __summary__, __title__, + __uri__, __version__, +) + + __all__ = [ - 'rand', 'crypto', 'SSL', 'tsafe', '__version__'] + "SSL", "crypto", "rand", "tsafe", + + "__author__", "__copyright__", "__email__", "__license__", "__summary__", + "__title__", "__uri__", "__version__", +] diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py index c7bdabc..7bb2910 100644 --- a/OpenSSL/crypto.py +++ b/OpenSSL/crypto.py @@ -159,6 +159,9 @@ def _get_asn1_time(timestamp): class PKey(object): + """ + A class representing an DSA or RSA public key or key pair. + """ _only_public = False _initialized = True @@ -170,12 +173,19 @@ class PKey(object): def generate_key(self, type, bits): """ - Generate a key of a given type, with a given number of a bits + Generate a key pair of the given type, with the given number of bits. - :param type: The key type (TYPE_RSA or TYPE_DSA) - :param bits: The number of bits + This generates a key "into" the this object. - :return: None + :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: :py:const:`None` """ if not isinstance(type, int): raise TypeError("type must be an integer") @@ -232,6 +242,8 @@ class PKey(object): """ 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 Error: if the key is inconsistent. :raise TypeError: if the key is of a type which cannot be checked. @@ -422,11 +434,35 @@ def get_elliptic_curve(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: An X509Name object to copy + :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) @@ -545,19 +581,23 @@ class X509Name(object): def hash(self): """ - Return the hash value of this name + 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: None + :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 encoding of this name. - :return: A :py:class:`bytes` instance giving the DER encoded form 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) @@ -572,9 +612,10 @@ class X509Name(object): def get_components(self): """ - Returns the split-up components of this name. + Returns the components of this name, as a sequence of 2-tuples. - :return: List of tuples (name, value). + :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)): @@ -593,27 +634,35 @@ class X509Name(object): _lib.ASN1_STRING_length(fval)))) return result + + + X509NameType = X509Name + class X509Extension(object): + """ + An X.509 v3 certificate extension. + """ def __init__(self, type_name, critical, value, subject=None, issuer=None): """ - :param typename: The name of the extension to create. + Initializes an X509 extension. + + :param typename: The name of the type of extension to create. See + http://openssl.org/docs/apps/x509v3_config.html#STANDARD_EXTENSIONS :type typename: :py:data:`str` - :param critical: A flag indicating whether this is a critical extension. + :param bool critical: A flag indicating whether this is a critical extension. :param value: The value of the extension. :type value: :py:data:`str` - :param subject: Optional X509 cert to use as subject. + :param subject: Optional X509 certificate to use as subject. :type subject: :py:class:`X509` - :param issuer: Optional X509 cert to use as issuer. + :param issuer: Optional X509 certificate to use as issuer. :type issuer: :py:class:`X509` - - :return: The X509Extension object """ ctx = _ffi.new("X509V3_CTX*") @@ -684,6 +733,7 @@ class X509Extension(object): "GENERAL_NAMES*", method.d2i(_ffi.NULL, payloadptr, length)) + 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) @@ -718,7 +768,7 @@ class X509Extension(object): def get_critical(self): """ - Returns the critical field of the X509Extension + Returns the critical field of this X.509 extension. :return: The critical field. """ @@ -727,9 +777,14 @@ class X509Extension(object): def get_short_name(self): """ - Returns the short version of the type name of the X509Extension + 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) @@ -738,9 +793,12 @@ class X509Extension(object): def get_data(self): """ - Returns the data of the X509Extension + Returns the data of the X509 extension, encoded as ASN.1. - :return: A :py:data:`str` giving the X509Extension's ASN.1 encoded data. + :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) @@ -748,10 +806,16 @@ class X509Extension(object): result_length = _lib.ASN1_STRING_length(string_result) return _ffi.buffer(char_result, result_length)[:] + + X509ExtensionType = X509Extension + 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) @@ -759,10 +823,12 @@ class X509Req(object): def set_pubkey(self, pkey): """ - Set the public key of the certificate request + Set the public key of the certificate signing request. - :param pkey: The public key to use - :return: None + :param pkey: The public key to use. + :type pkey: :py:class:`PKey` + + :return: :py:const:`None` """ set_result = _lib.X509_REQ_set_pubkey(self._req, pkey._pkey) if not set_result: @@ -772,9 +838,10 @@ class X509Req(object): def get_pubkey(self): """ - Get the public key from the certificate request + Get the public key of the certificate signing request. - :return: The public key + :return: The public key. + :rtype: :py:class:`PKey` """ pkey = PKey.__new__(PKey) pkey._pkey = _lib.X509_REQ_get_pubkey(self._req) @@ -791,8 +858,8 @@ class X509Req(object): Set the version subfield (RFC 2459, section 4.1.2.1) of the certificate request. - :param version: The version number - :return: None + :param int version: The version number. + :return: :py:const:`None` """ set_result = _lib.X509_REQ_set_version(self._req, version) if not set_result: @@ -804,16 +871,21 @@ class X509Req(object): Get the version subfield (RFC 2459, section 4.1.2.1) of the certificate request. - :return: an integer giving the value of the version subfield + :return: The value of the version subfield. + :rtype: :py:class:`int` """ return _lib.X509_REQ_get_version(self._req) def get_subject(self): """ - Create an X509Name object for the subject of the certificate request + Return the subject of this certificate signing request. + + This creates a new :py:class:`X509Name`: modifying it does not affect + this request. - :return: An X509Name object + :return: The subject of this certificate signing request. + :rtype: :py:class:`X509Name` """ name = X509Name.__new__(X509Name) name._name = _lib.X509_REQ_get_subject_name(self._req) @@ -830,10 +902,11 @@ class X509Req(object): def add_extensions(self, extensions): """ - Add extensions to the request. + Add extensions to the certificate signing request. - :param extensions: a sequence of X509Extension objects - :return: None + :param extensions: The X.509 extensions to add. + :type extensions: iterable of :py:class:`X509Extension` + :return: :py:const:`None` """ stack = _lib.sk_X509_EXTENSION_new_null() if stack == _ffi.NULL: @@ -857,9 +930,12 @@ class X509Req(object): def get_extensions(self): """ - Get extensions to the request. + 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. - :return: A :py:class:`list` of :py:class:`X509Extension` objects. + .. versionadded:: 0.15 """ exts = [] native_exts_obj = _lib.X509_REQ_get_extensions(self._req) @@ -872,11 +948,14 @@ class X509Req(object): def sign(self, pkey, digest): """ - Sign the certificate request using the supplied key and digest + Sign the certificate signing request with this key and digest type. - :param pkey: The key to sign with - :param digest: The message digest to use - :return: None + :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"sha1"`. + :type digest: :py:class:`bytes` + :return: :py:const:`None` """ if pkey._only_public: raise ValueError("Key has only public part") @@ -896,12 +975,13 @@ class X509Req(object): def verify(self, pkey): """ - Verifies a certificate request using the supplied public key - - :param key: a public key - :return: True if the signature is correct. + Verifies the signature on this certificate signing request. - :raise OpenSSL.crypto.Error: If the signature is invalid or there is a + :param key: A public key. + :type key: :py:class:`PKey` + :return: :py:data:`True` if the signature is correct. + :rtype: :py:class:`bool` + :raises Error: If the signature is invalid or there is a problem verifying the signature. """ if not isinstance(pkey, PKey): @@ -914,11 +994,15 @@ class X509Req(object): return result + X509ReqType = X509Req class X509(object): + """ + An X.509 certificate. + """ def __init__(self): # TODO Allocation failure? And why not __new__ instead of __init__? x509 = _lib.X509_new() @@ -927,12 +1011,12 @@ class X509(object): def set_version(self, version): """ - Set version number of the certificate + Set the version number of the certificate. - :param version: The version number + :param version: The version number of the certificate. :type version: :py:class:`int` - :return: None + :return: :py:const:`None` """ if not isinstance(version, int): raise TypeError("version must be an integer") @@ -942,18 +1026,20 @@ class X509(object): def get_version(self): """ - Return version number of the certificate + Return the version number of the certificate. - :return: Version number as a Python integer + :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 + Get the public key of the certificate. - :return: The public key + :return: The public key. + :rtype: :py:class:`PKey` """ pkey = PKey.__new__(PKey) pkey._pkey = _lib.X509_get_pubkey(self._x509) @@ -966,11 +1052,12 @@ class X509(object): def set_pubkey(self, pkey): """ - Set the public key of the certificate + Set the public key of the certificate. - :param pkey: The public key + :param pkey: The public key. + :type pkey: :py:class:`PKey` - :return: None + :return: :py:data:`None` """ if not isinstance(pkey, PKey): raise TypeError("pkey must be a PKey instance") @@ -982,11 +1069,15 @@ class X509(object): def sign(self, pkey, digest): """ - Sign the certificate using the supplied key and digest + Sign the certificate with this key and digest type. + + :param pkey: The key to sign with. + :type pkey: :py:class:`PKey` - :param pkey: The key to sign with - :param digest: The message digest to use - :return: None + :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") @@ -1008,11 +1099,14 @@ class X509(object): def get_signature_algorithm(self): """ - Retrieve the signature algorithm used in the certificate + 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. - :return: A byte string giving the name of the signature algorithm used in - the certificate. - :raise ValueError: If the signature algorithm is undefined. + .. versionadded:: 0.13 """ alg = self._x509.cert_info.signature.algorithm nid = _lib.OBJ_obj2nid(alg) @@ -1028,7 +1122,9 @@ class 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 + :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: @@ -1055,18 +1151,19 @@ class X509(object): 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 serial number of the certificate + Set the serial number of the certificate. - :param serial: The serial number + :param serial: The new serial number. :type serial: :py:class:`int` - :return: None + :return: :py:data`None` """ if not isinstance(serial, _integer_types): raise TypeError("serial must be an integer") @@ -1103,9 +1200,10 @@ class X509(object): def get_serial_number(self): """ - Return serial number of the certificate + Return the serial number of this certificate. - :return: Serial number as a Python integer + :return: The serial number. + :rtype: :py:class:`int` """ asn1_serial = _lib.X509_get_serialNumber(self._x509) bignum_serial = _lib.ASN1_INTEGER_to_BN(asn1_serial, _ffi.NULL) @@ -1123,13 +1221,12 @@ class X509(object): def gmtime_adj_notAfter(self, amount): """ - Adjust the time stamp for when the certificate stops being valid + Adjust the time stamp on which the certificate stops being valid. - :param amount: The number of seconds by which to adjust the ending - validity time. + :param amount: The number of seconds by which to adjust the timestamp. :type amount: :py:class:`int` - :return: None + :return: :py:const:`None` """ if not isinstance(amount, int): raise TypeError("amount must be an integer") @@ -1140,12 +1237,10 @@ class X509(object): def gmtime_adj_notBefore(self, amount): """ - Change the timestamp for when the certificate starts being valid to the current - time plus an offset. + Adjust the timestamp on which the certificate starts being valid. - :param amount: The number of seconds by which to adjust the starting validity - time. - :return: None + :param amount: The number of seconds by which to adjust the timestamp. + :return: :py:const:`None` """ if not isinstance(amount, int): raise TypeError("amount must be an integer") @@ -1158,7 +1253,9 @@ class X509(object): """ Check whether the certificate has expired. - :return: True if the certificate has expired, false otherwise + :return: :py:const:`True` if the certificate has expired, + :py:const:`False` otherwise. + :rtype: :py:class:`bool` """ now = int(time()) notAfter = _lib.X509_get_notAfter(self._x509) @@ -1172,15 +1269,16 @@ class X509(object): def get_notBefore(self): """ - Retrieve the time stamp for when the certificate starts being valid + Get the timestamp at which the certificate starts being valid. - :return: A string giving the timestamp, in the format:: + The timestamp is formatted as an ASN.1 GENERALIZEDTIME:: - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm - or None if there is no value set. + :return: A timestamp string, or :py:const:`None` if there is none. + :rtype: :py:class:`bytes` or :py:const:`None` """ return self._get_boundary_time(_lib.X509_get_notBefore) @@ -1191,47 +1289,52 @@ class X509(object): def set_notBefore(self, when): """ - Set the time stamp for when the certificate starts being valid + Set the timestamp at which the certificate starts being valid. - :param when: A string giving the timestamp, in the format: + The timestamp is formatted as an ASN.1 GENERALIZEDTIME:: - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm + + :param when: A timestamp string. :type when: :py:class:`bytes` - :return: None + :return: :py:const:`None` """ return self._set_boundary_time(_lib.X509_get_notBefore, when) def get_notAfter(self): """ - Retrieve the time stamp for when the certificate stops being valid + Get the timestamp at which the certificate stops being valid. - :return: A string giving the timestamp, in the format:: + The timestamp is formatted as an ASN.1 GENERALIZEDTIME:: - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm - or None if there is no value set. + :return: A timestamp string, or :py:const:`None` if there is none. + :rtype: :py:class:`bytes` or :py:const:`None` """ return self._get_boundary_time(_lib.X509_get_notAfter) def set_notAfter(self, when): """ - Set the time stamp for when the certificate stops being valid + Set the timestamp at which the certificate stops being valid. + + The timestamp is formatted as an ASN.1 GENERALIZEDTIME:: - :param when: A string giving the timestamp, in the format: + YYYYMMDDhhmmssZ + YYYYMMDDhhmmss+hhmm + YYYYMMDDhhmmss-hhmm - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm + :param when: A timestamp string. :type when: :py:class:`bytes` - :return: None + :return: :py:const:`None` """ return self._set_boundary_time(_lib.X509_get_notAfter, when) @@ -1261,50 +1364,62 @@ class X509(object): def get_issuer(self): """ - Create an X509Name object for the issuer of the certificate + Return the issuer of this certificate. + + This creates a new :py:class:`X509Name`: modifying it does not affect + this certificate. - :return: An X509Name object + :return: The issuer of this certificate. + :rtype: :py:class:`X509Name` """ return self._get_name(_lib.X509_get_issuer_name) def set_issuer(self, issuer): """ - Set the issuer of the certificate + Set the issuer of this certificate. - :param issuer: The issuer name + :param issuer: The issuer. :type issuer: :py:class:`X509Name` - :return: None + :return: :py:const:`None` """ return self._set_name(_lib.X509_set_issuer_name, issuer) def get_subject(self): """ - Create an X509Name object for the subject of the certificate + Return the subject of this certificate. - :return: An X509Name object + This creates a new :py:class:`X509Name`: modifying it does not affect + this certificate. + + :return: The subject of this certificate. + :rtype: :py:class:`X509Name` """ return self._get_name(_lib.X509_get_subject_name) def set_subject(self, subject): """ - Set the subject of the certificate + Set the subject of this certificate. - :param subject: The subject name + :param subject: The subject. :type subject: :py:class:`X509Name` - :return: None + + :return: :py:const:`None` """ return self._set_name(_lib.X509_set_subject_name, subject) def get_extension_count(self): """ - Get the number of extensions on the certificate. + Get the number of extensions on this certificate. + + :return: The number of extensions. + :rtype: :py:class:`int` - :return: The number of extensions as an integer. + .. versionadded:: 0.12 """ return _lib.X509_get_ext_count(self._x509) @@ -1313,8 +1428,9 @@ class X509(object): """ Add extensions to the certificate. - :param extensions: a sequence of X509Extension objects - :return: None + :param extensions: The extensions to add. + :type extensions: An iterable of :py:class:`X509Extension` objects. + :return: :py:const:`None` """ for ext in extensions: if not isinstance(ext, X509Extension): @@ -1329,8 +1445,15 @@ class X509(object): """ Get a specific extension of the certificate by index. - :param index: The index of the extension to retrieve. - :return: The X509Extension object at the specified index. + 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) @@ -1341,17 +1464,32 @@ class X509(object): ext._extension = _ffi.gc(extension, _lib.X509_EXTENSION_free) return ext + + X509Type = X509 class X509Store(object): + """ + An X509 certificate store. + """ def __init__(self): store = _lib.X509_STORE_new() self._store = _ffi.gc(store, _lib.X509_STORE_free) def add_cert(self, cert): + """ + Adds the certificate :py:data:`cert` to this store. + + This is the Python equivalent of OpenSSL's ``X509_STORE_add_cert``. + + :param X509 cert: The certificate to add to this store. + :raises TypeError: If the certificate is not an :py:class:`X509`. + :raises Error: If OpenSSL was unhappy with your certificate. + :return: :py:data:`None` if the certificate was added successfully. + """ if not isinstance(cert, X509): raise TypeError() @@ -1621,6 +1759,9 @@ def _X509_REVOKED_dup(original): class Revoked(object): + """ + A certificate revocation. + """ # 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 @@ -1643,11 +1784,15 @@ class Revoked(object): def set_serial(self, hex_str): """ - Set the serial number of a revoked Revoked structure + Set the serial number. + + The serial number is formatted as a hexadecimal number encoded in + ASCII. :param hex_str: The new serial number. - :type hex_str: :py:data:`str` - :return: None + :type hex_str: :py:class:`bytes` + + :return: :py:const:`None` """ bignum_serial = _ffi.gc(_lib.BN_new(), _lib.BN_free) bignum_ptr = _ffi.new("BIGNUM**") @@ -1664,9 +1809,13 @@ class Revoked(object): def get_serial(self): """ - Return the serial number of a Revoked structure + Get the serial number. + + The serial number is formatted as a hexadecimal number encoded in + ASCII. - :return: The serial number as a string + :return: The serial number. + :rtype: :py:class:`bytes` """ bio = _new_mem_buf() @@ -1690,13 +1839,19 @@ class Revoked(object): def set_reason(self, reason): """ - Set the reason of a Revoked object. + Set the reason of this revocation. - If :py:data:`reason` is :py:data:`None`, delete the reason instead. + If :py:data:`reason` is :py:const:`None`, delete the reason instead. :param reason: The reason string. - :type reason: :py:class:`str` or :py:class:`NoneType` - :return: None + :type reason: :py:class:`bytes` or :py:class:`NoneType` + + :return: :py:const:`None` + + .. seealso:: + + :py: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() @@ -1728,9 +1883,15 @@ class Revoked(object): def get_reason(self): """ - Return the reason of a Revoked object. + Set the reason of this revocation. + + :return: The reason, or :py:const:`None` if there is none. + :rtype: :py:class:`bytes` or :py:class:`NoneType` + + .. seealso:: - :return: The reason as a string + :py:meth:`all_reasons`, which gives you a list of all supported + reasons this method might return. """ extensions = self._revoked.extensions for i in range(_lib.sk_X509_EXTENSION_num(extensions)): @@ -1752,44 +1913,44 @@ class Revoked(object): """ 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: :py:class:`list` of :py:class:`bytes` """ return self._crl_reasons[:] def set_rev_date(self, when): """ - Set the revocation timestamp - - :param when: A string giving the timestamp, in the format: - - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm + Set the revocation timestamp. - :return: None + :param when: The timestamp of the revocation, as ASN.1 GENERALIZEDTIME. + :type when: :py:class:`bytes` + :return: :py:const:`None` """ return _set_asn1_time(self._revoked.revocationDate, when) def get_rev_date(self): """ - Retrieve the revocation date - - :return: A string giving the timestamp, in the format: + Get the revocation timestamp. - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm + :return: The timestamp of the revocation, as ASN.1 GENERALIZEDTIME. + :rtype: :py:class:`bytes` """ return _get_asn1_time(self._revoked.revocationDate) class CRL(object): + """ + A certificate revocation list. + """ def __init__(self): """ - Create a new empty CRL object. + Create a new empty certificate revocation list. """ crl = _lib.X509_CRL_new() self._crl = _ffi.gc(crl, _lib.X509_CRL_free) @@ -1797,9 +1958,13 @@ class CRL(object): def get_revoked(self): """ - Return revoked portion of the CRL structure (by value not reference). + 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: A tuple of Revoked objects. + :return: The revocations in this CRL. + :rtype: :py:class:`tuple` of :py:class:`Revocation` """ results = [] revoked_stack = self._crl.crl.revoked @@ -1817,10 +1982,14 @@ class CRL(object): """ Add a revoked (by value not reference) to the CRL structure - :param revoked: The new revoked. - :type revoked: :class:`X509` + 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. - :return: None + :param revoked: The new revocation. + :type revoked: :class:`Revoked` + + :return: :py:const:`None` """ copy = _X509_REVOKED_dup(revoked._revoked) if copy == _ffi.NULL: @@ -1836,13 +2005,13 @@ class CRL(object): def export(self, cert, key, type=FILETYPE_PEM, days=100, digest=_UNSPECIFIED): """ - export a CRL as a string + Export a CRL as a string. - :param cert: Used to sign CRL. - :type cert: :class:`X509` + :param cert: The certificate used to sign the CRL. + :type cert: :py:class:`X509` - :param key: Used to sign CRL. - :type key: :class:`PKey` + :param key: The key used to sign the CRL. + :type key: :py:class:`PKey` :param type: The export format, either :py:data:`FILETYPE_PEM`, :py:data:`FILETYPE_ASN1`, or :py:data:`FILETYPE_TEXT`. @@ -1976,6 +2145,9 @@ PKCS7Type = PKCS7 class PKCS12(object): + """ + A PKCS #12 archive. + """ def __init__(self): self._pkey = None self._cert = None @@ -1985,20 +2157,22 @@ class PKCS12(object): def get_certificate(self): """ - Return certificate portion of the PKCS12 structure + Get the certificate in the PKCS #12 structure. - :return: X509 object containing the certificate + :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): """ - Replace the certificate portion of the PKCS12 structure + 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` - :param cert: The new certificate. - :type cert: :py:class:`X509` or :py:data:`None` - :return: None + :return: :py:const:`None` """ if not isinstance(cert, X509): raise TypeError("cert must be an X509 instance") @@ -2007,20 +2181,22 @@ class PKCS12(object): def get_privatekey(self): """ - Return private key portion of the PKCS12 structure + Get the private key in the PKCS #12 structure. - :returns: PKey object containing the private key + :return: The private key, or :py:const:`None` if there is none. + :rtype: :py:class:`PKey` """ return self._pkey def set_privatekey(self, pkey): """ - Replace or set the certificate portion of the PKCS12 structure + Set the certificate portion of the PKCS #12 structure. - :param pkey: The new private key. - :type pkey: :py:class:`PKey` - :return: None + :param pkey: The new private key, or :py:const:`None` to unset it. + :type pkey: :py:class:`PKey` or :py:const:`None` + + :return: :py:const:`None` """ if not isinstance(pkey, PKey): raise TypeError("pkey must be a PKey instance") @@ -2029,10 +2205,11 @@ class PKCS12(object): def get_ca_certificates(self): """ - Return CA certificates within of the PKCS12 object + Get the CA certificates in the PKCS #12 structure. - :return: A newly created tuple containing the CA certificates in the chain, - if any are present, or None if no CA certificates are present. + :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) @@ -2042,9 +2219,11 @@ class PKCS12(object): """ Replace or set the CA certificates within the PKCS12 object. - :param cacerts: The new CA certificates. - :type cacerts: :py:data:`None` or an iterable of :py:class:`X509` - :return: None + :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: :py:const:`None` """ if cacerts is None: self._cacerts = None @@ -2058,11 +2237,12 @@ class PKCS12(object): def set_friendlyname(self, name): """ - Replace or set the certificate portion of the PKCS12 structure + 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` - :param name: The new friendly name. - :type name: :py:class:`bytes` - :return: None + :return: :py:const:`None` """ if name is None: self._friendlyname = None @@ -2073,27 +2253,33 @@ class PKCS12(object): def get_friendlyname(self): """ - Return friendly name portion of the PKCS12 structure + Get the friendly name in the PKCS# 12 structure. - :returns: String containing the friendlyname + :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. See also "man PKCS12_create". + Dump a PKCS12 object as a string. - :param passphrase: used to encrypt the PKCS12 + 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: How many times to repeat the encryption + :param iter: Number of times to repeat the encryption step. :type iter: :py:data:`int` - :param maciter: How many times to repeat the MAC + :param maciter: Number of times to repeat the MAC step. :type maciter: :py:data:`int` - :return: The string containing the PKCS12 + :return: The string representation of the PKCS #12 structure. + :rtype: """ passphrase = _text_to_bytes_and_warn("passphrase", passphrase) @@ -2135,11 +2321,16 @@ class PKCS12(object): _lib.i2d_PKCS12_bio(bio, pkcs12) return _bio_to_string(bio) + + PKCS12Type = PKCS12 class NetscapeSPKI(object): + """ + A Netscape SPKI object. + """ def __init__(self): spki = _lib.NETSCAPE_SPKI_new() self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free) @@ -2147,11 +2338,15 @@ class NetscapeSPKI(object): def sign(self, pkey, digest): """ - Sign the certificate request using the supplied key and 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 pkey: The key to sign with - :param digest: The message digest to use - :return: None + :param digest: The message digest to use. + :type digest: :py:class:`bytes` + + :return: :py:const:`None` """ if pkey._only_public: raise ValueError("Key has only public part") @@ -2171,12 +2366,16 @@ class NetscapeSPKI(object): def verify(self, key): """ - Verifies a certificate request using the supplied public key + Verifies a signature on a certificate request. - :param key: a public key - :return: True if the signature is correct. - :raise OpenSSL.crypto.Error: If the signature is invalid or there is a - problem verifying the signature. + :param key: The public key that signature is supposedly from. + :type pkey: :py:class:`PKey` + + :return: :py:const:`True` if the signature is correct. + :rtype: :py:class:`bool` + + :raises 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: @@ -2186,9 +2385,10 @@ class NetscapeSPKI(object): def b64_encode(self): """ - Generate a base64 encoded string from an SPKI + Generate a base64 encoded representation of this SPKI object. - :return: The base64 encoded string + :return: The base64 encoded string. + :rtype: :py:class:`bytes` """ encoded = _lib.NETSCAPE_SPKI_b64_encode(self._spki) result = _ffi.string(encoded) @@ -2198,9 +2398,10 @@ class NetscapeSPKI(object): def get_pubkey(self): """ - Get the public key of the certificate + Get the public key of this certificate. - :return: The public key + :return: The public key. + :rtype: :py:class:`PKey` """ pkey = PKey.__new__(PKey) pkey._pkey = _lib.NETSCAPE_SPKI_get_pubkey(self._spki) @@ -2217,15 +2418,19 @@ class NetscapeSPKI(object): Set the public key of the certificate :param pkey: The public key - :return: None + :return: :py:const:`None` """ set_result = _lib.NETSCAPE_SPKI_set_pubkey(self._spki, pkey._pkey) if not set_result: # TODO: This is untested. _raise_current_error() + + + NetscapeSPKIType = NetscapeSPKI + class _PassphraseHelper(object): def __init__(self, type, passphrase, more_args=False, truncate=False): if type != FILETYPE_PEM and passphrase is not None: @@ -2422,13 +2627,13 @@ def sign(pkey, data, digest): def verify(cert, signature, data, digest): """ - Verify a signature + Verify a signature. :param cert: signing certificate (X509 object) :param signature: signature returned by sign function :param data: data to be verified :param digest: message digest to use - :return: None if the signature is correct, raise exception otherwise + :return: :py:const:`None` if the signature is correct, raise exception otherwise """ data = _text_to_bytes_and_warn("data", data) diff --git a/OpenSSL/version.py b/OpenSSL/version.py index eb3b736..f284b04 100644 --- a/OpenSSL/version.py +++ b/OpenSSL/version.py @@ -6,4 +6,17 @@ pyOpenSSL - A simple wrapper around the OpenSSL library """ -__version__ = '0.15.1' +__all__ = [ + "__author__", "__copyright__", "__email__", "__license__", "__summary__", + "__title__", "__uri__", "__version__", +] + +__version__ = "0.16.dev0" + +__title__ = "pyOpenSSL" +__uri__ = "https://github.com/pyca/pyopenssl" +__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-2015 {0}".format(__author__) @@ -1,6 +1,5 @@ - -pyOpenSSL - A Python wrapper around the OpenSSL library -------------------------------------------------------- +pyOpenSSL -- A Python wrapper around the OpenSSL library +-------------------------------------------------------- .. image:: https://coveralls.io/repos/pyca/pyopenssl/badge.svg :target: https://coveralls.io/r/pyca/pyopenssl @@ -12,6 +11,15 @@ pyOpenSSL - A Python wrapper around the OpenSSL library .. image:: https://travis-ci.org/pyca/pyopenssl.svg?branch=master :target: https://travis-ci.org/pyca/pyopenssl + +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. + See the file INSTALL.rst for installation instructions. See https://github.com/pyca/pyopenssl for development. @@ -1,8 +0,0 @@ -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? diff --git a/doc/api/crypto.rst b/doc/api/crypto.rst index 57a60f3..101f3bd 100644 --- a/doc/api/crypto.rst +++ b/doc/api/crypto.rst @@ -6,128 +6,8 @@ .. py:module:: OpenSSL.crypto :synopsis: Generic cryptographic module - -.. py:data:: X509Type - - See :py:class:`X509`. - - -.. py:class:: X509() - - A class representing X.509 certificates. - - -.. py:data:: X509NameType - - See :py:class:`X509Name`. - - -.. py:class:: X509Name(x509name) - - A class representing X.509 Distinguished Names. - - This constructor creates a copy of *x509name* which should be an - instance of :py:class:`X509Name`. - - -.. py:data:: X509ReqType - - See :py:class:`X509Req`. - - -.. py:class:: X509Req() - - A class representing X.509 certificate requests. - - -.. py:data:: X509StoreType - - See :py:class:`X509Store` - - -.. py:data X509Store - - A class representing the X.509 store. - - -.. py:data:: X509StoreContext - - A class representing the X.509 store context. - - -.. py:data:: PKeyType - - See :py:class:`PKey`. - - -.. py:class:: PKey() - - A class representing DSA or RSA keys. - - -.. py:data:: PKCS7Type - - A Python type object representing the PKCS7 object type. - - -.. py:data:: PKCS12Type - - A Python type object representing the PKCS12 object type. - - -.. py:data:: X509ExtensionType - - See :py:class:`X509Extension`. - - -.. py:class:: X509Extension(typename, critical, value[, subject][, issuer]) - - A class representing an X.509 v3 certificate extensions. See - http://openssl.org/docs/apps/x509v3_config.html#STANDARD_EXTENSIONS for - *typename* strings and their options. Optional parameters *subject* and - *issuer* must be X509 objects. - - -.. py:data:: NetscapeSPKIType - - See :py:class:`NetscapeSPKI`. - - -.. py:class:: NetscapeSPKI([enc]) - - A class representing Netscape SPKI objects. - - If the *enc* argument is present, it should be a base64-encoded string - representing a NetscapeSPKI object, as returned by the :py:meth:`b64_encode` - method. - - -.. py:class:: CRL() - - A class representing Certifcate Revocation List objects. - - -.. py:class:: Revoked() - - A class representing Revocation objects of CRL. - - -.. py:data:: FILETYPE_PEM - FILETYPE_ASN1 - - File type constants. - - -.. py:data:: TYPE_RSA - TYPE_DSA - - Key type constants. - - -.. py:exception:: Error - - Generic exception used in the :py:mod:`.crypto` module. - +Elliptic curves +--------------- .. py:function:: get_elliptic_curves @@ -151,17 +31,43 @@ If the named curve is not supported then :py:class:`ValueError` is raised. +Serialization and deserialization +--------------------------------- + +The following serialization functions take one of these constants to +determine the format: + +.. py:data:: FILETYPE_PEM + FILETYPE_ASN1 + +Certificates +~~~~~~~~~~~~ + .. py:function:: dump_certificate(type, cert) Dump the certificate *cert* into a buffer string encoded with the type *type*. +.. py:function:: load_certificate(type, buffer) + + Load a certificate (X509) from the string *buffer* encoded with the + type *type*. + +Certificate signing requests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. py:function:: dump_certificate_request(type, req) Dump the certificate request *req* into a buffer string encoded with the type *type*. +.. py:function:: load_certificate_request(type, buffer) + + Load a certificate request (X509Req) from the string *buffer* encoded with + the type *type*. + +Private keys +~~~~~~~~~~~~ .. py:function:: dump_privatekey(type, pkey[, cipher, passphrase]) @@ -172,19 +78,6 @@ *passphrase* must be either a string or a callback for providing the pass phrase. - -.. py:function:: load_certificate(type, buffer) - - Load a certificate (X509) from the string *buffer* encoded with the - type *type*. - - -.. py:function:: load_certificate_request(type, buffer) - - Load a certificate request (X509Req) from the string *buffer* encoded with - the type *type*. - - .. py:function:: load_privatekey(type, buffer[, passphrase]) Load a private key (PKey) from the string *buffer* encoded with the type @@ -194,6 +87,8 @@ *passphrase* must be either a string or a callback for providing the pass phrase. +Certificate revocation lists +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. py:function:: load_crl(type, buffer) @@ -204,7 +99,9 @@ .. py:function:: load_pkcs7_data(type, buffer) - Load pkcs7 data from the string *buffer* encoded with the type *type*. + Load pkcs7 data from the string *buffer* encoded with the type + *type*. The type *type* must either :py:const:`FILETYPE_PEM` or + :py:const:`FILETYPE_ASN1`). .. py:function:: load_pkcs12(buffer[, passphrase]) @@ -215,6 +112,8 @@ See also the man page for the C function :py:func:`PKCS12_parse`. +Signing and verifying signatures +-------------------------------- .. py:function:: sign(key, data, digest) @@ -245,296 +144,36 @@ X509 objects ------------ -X509 objects have the following methods: - -.. py:method:: X509.get_issuer() - - Return an X509Name object representing the issuer of the certificate. - - -.. py:method:: X509.get_pubkey() - - Return a :py:class:`PKey` object representing the public key of the certificate. - - -.. py:method:: X509.get_serial_number() - - Return the certificate serial number. - - -.. py:method:: X509.get_signature_algorithm() - - Return the signature algorithm used in the certificate. If the algorithm is - undefined, raise :py:data:`ValueError`. - - .. versionadded:: 0.13 - - -.. py:method:: X509.get_subject() - - Return an :py:class:`X509Name` object representing the subject of the certificate. - - -.. py:method:: X509.get_version() - - Return the certificate version. - - -.. py:method:: X509.get_notBefore() - - Return a string giving the time before which the certificate is not valid. The - string is formatted as an ASN1 GENERALIZEDTIME:: - - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm - - If no value exists for this field, :py:data:`None` is returned. - - -.. py:method:: X509.get_notAfter() - - Return a string giving the time after which the certificate is not valid. The - string is formatted as an ASN1 GENERALIZEDTIME:: - - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm - - If no value exists for this field, :py:data:`None` is returned. - - -.. py:method:: X509.set_notBefore(when) - - Change the time before which the certificate is not valid. *when* is a - string formatted as an ASN1 GENERALIZEDTIME:: - - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm - - -.. py:method:: X509.set_notAfter(when) - - Change the time after which the certificate is not valid. *when* is a - string formatted as an ASN1 GENERALIZEDTIME:: - - YYYYMMDDhhmmssZ - YYYYMMDDhhmmss+hhmm - YYYYMMDDhhmmss-hhmm - - - -.. py:method:: X509.gmtime_adj_notBefore(time) - - Adjust the timestamp (in GMT) when the certificate starts being valid. - - -.. py:method:: X509.gmtime_adj_notAfter(time) - - Adjust the timestamp (in GMT) when the certificate stops being valid. - - -.. py:method:: X509.has_expired() - - Checks the certificate's time stamp against current time. Returns true if the - certificate has expired and false otherwise. - - -.. py:method:: X509.set_issuer(issuer) - - Set the issuer of the certificate to *issuer*. - - -.. py:method:: X509.set_pubkey(pkey) - - Set the public key of the certificate to *pkey*. - - -.. py:method:: X509.set_serial_number(serialno) - - Set the serial number of the certificate to *serialno*. - - -.. py:method:: X509.set_subject(subject) - - Set the subject of the certificate to *subject*. - - -.. py:method:: X509.set_version(version) - - Set the certificate version to *version*. - - -.. py:method:: X509.sign(pkey, digest) - - Sign the certificate, using the key *pkey* and the message digest algorithm - identified by the string *digest*. - - -.. py:method:: X509.subject_name_hash() - - Return the hash of the certificate subject. - -.. py:method:: X509.digest(digest_name) - - Return a digest of the certificate, using the *digest_name* method. - *digest_name* must be a string describing a digest algorithm supported - by OpenSSL (by EVP_get_digestbyname, specifically). For example, - :py:const:`"md5"` or :py:const:`"sha1"`. - - -.. py:method:: X509.add_extensions(extensions) - - Add the extensions in the sequence *extensions* to the certificate. - - -.. py:method:: X509.get_extension_count() - - Return the number of extensions on this certificate. - - .. versionadded:: 0.12 - - -.. py:method:: X509.get_extension(index) - - Retrieve the extension on this certificate at the given index. - - Extensions on a certificate are kept in order. The index parameter selects - which extension will be returned. The returned object will be an - :py:class:`X509Extension` instance. - - .. versionadded:: 0.12 - +.. autoclass:: X509 + :members: .. _openssl-x509name: X509Name objects ---------------- -X509Name objects have the following methods: - -.. py:method:: X509Name.hash() - - Return an integer giving the first four bytes of the MD5 digest of the DER - representation of the name. - - -.. py:method:: X509Name.der() - - Return a string giving the DER representation of the name. - - -.. py:method:: X509Name.get_components() - - Return a list of two-tuples of strings giving the components of the name. - - -X509Name objects have the following members: - -.. py:attribute:: X509Name.countryName - - The country of the entity. :py:attr:`C` may be used as an alias for - :py:attr:`countryName`. - - -.. py:attribute:: X509Name.stateOrProvinceName - - The state or province of the entity. :py:attr:`ST` may be used as an alias for - :py:attr:`stateOrProvinceName`. - - -.. py:attribute:: X509Name.localityName - - The locality of the entity. :py:attr:`L` may be used as an alias for - :py:attr:`localityName`. - - -.. py:attribute:: X509Name.organizationName - - The organization name of the entity. :py:attr:`O` may be used as an alias for - :py:attr:`organizationName`. - - -.. py:attribute:: X509Name.organizationalUnitName - - The organizational unit of the entity. :py:attr:`OU` may be used as an alias for - :py:attr:`organizationalUnitName`. - - -.. py:attribute:: X509Name.commonName - - The common name of the entity. :py:attr:`CN` may be used as an alias for - :py:attr:`commonName`. - - -.. py:attribute:: X509Name.emailAddress - - The e-mail address of the entity. - +.. autoclass:: X509Name + :members: + :special-members: + :exclude-members: __repr__, __getattr__, __weakref__ .. _openssl-x509req: X509Req objects --------------- -X509Req objects have the following methods: - -.. py:method:: X509Req.get_pubkey() - - Return a :py:class:`PKey` object representing the public key of the certificate request. - - -.. py:method:: X509Req.get_subject() - - Return an :py:class:`X509Name` object representing the subject of the certificate. - - -.. py:method:: X509Req.set_pubkey(pkey) - - Set the public key of the certificate request to *pkey*. - - -.. py:method:: X509Req.sign(pkey, digest) - - Sign the certificate request, using the key *pkey* and the message digest - algorithm identified by the string *digest*. - - -.. py:method:: X509Req.verify(pkey) - - Verify a certificate request using the public key *pkey*. - - -.. py:method:: X509Req.set_version(version) - - Set the version (RFC 2459, 4.1.2.1) of the certificate request to - *version*. - - -.. py:method:: X509Req.get_version() - - Get the version (RFC 2459, 4.1.2.1) of the certificate request. - - -.. py:method:: X509Req.get_extensions() - - Get extensions to the request. - - .. versionadded:: 0.15 - +.. autoclass:: X509Req + :members: + :special-members: + :exclude-members: __weakref__ .. _openssl-x509store: X509Store objects ----------------- -The X509Store object has currently just one method: - -.. py:method:: X509Store.add_cert(cert) - - Add the certificate *cert* to the certificate store. - +.. autoclass:: X509Store + :members: X509StoreContextError objects ----------------------------- @@ -547,8 +186,8 @@ The certificate for which the verification error was detected is given by the ``certificate`` attribute of the exception instance as a :class:`X509` instance. -Details about the verification error are given in the exception's ``args`` attribute. - +Details about the verification error are given in the exception's +``args`` attribute. X509StoreContext objects ------------------------ @@ -565,38 +204,20 @@ of trusted certificates. .. versionadded:: 0.15 - .. _openssl-pkey: PKey objects ------------ -The PKey object has the following methods: - -.. py:method:: PKey.bits() - - Return the number of bits of the key. - - -.. py:method:: PKey.generate_key(type, bits) - - Generate a public/private key pair of the type *type* (one of - :py:const:`TYPE_RSA` and :py:const:`TYPE_DSA`) with the size *bits*. - +.. autoclass:: PKey + :members: -.. py:method:: PKey.type() - - Return the type of the key. - - -.. py:method:: PKey.check() - - Check the consistency of this key, returning True if it is consistent and - raising an exception otherwise. This is only valid for RSA keys. See the - OpenSSL RSA_check_key man page for further limitations. +.. _openssl-pkcs7: +.. py:data:: TYPE_RSA + TYPE_DSA -.. _openssl-pkcs7: + Key type constants. PKCS7 objects ------------- @@ -607,217 +228,118 @@ PKCS7 objects have the following methods: FIXME - .. py:method:: PKCS7.type_is_enveloped() FIXME - .. py:method:: PKCS7.type_is_signedAndEnveloped() FIXME - .. py:method:: PKCS7.type_is_data() FIXME - .. py:method:: PKCS7.get_type_name() Get the type name of the PKCS7. - .. _openssl-pkcs12: PKCS12 objects -------------- -PKCS12 objects have the following methods: - -.. py:method:: PKCS12.export([passphrase=None][, iter=2048][, maciter=1]) - - Returns a PKCS12 object as a string. - - The optional *passphrase* must be a string not a callback. - - See also the man page for the C function :py:func:`PKCS12_create`. - - -.. py:method:: PKCS12.get_ca_certificates() - - Return CA certificates within the PKCS12 object as a tuple. Returns - :py:const:`None` if no CA certificates are present. - - -.. py:method:: PKCS12.get_certificate() - - Return certificate portion of the PKCS12 structure. - - -.. py:method:: PKCS12.get_friendlyname() - - Return friendlyName portion of the PKCS12 structure. - - -.. py:method:: PKCS12.get_privatekey() - - Return private key portion of the PKCS12 structure - - -.. py:method:: PKCS12.set_ca_certificates(cacerts) - - Replace or set the CA certificates within the PKCS12 object with the sequence *cacerts*. - - Set *cacerts* to :py:const:`None` to remove all CA certificates. - - -.. py:method:: PKCS12.set_certificate(cert) - - Replace or set the certificate portion of the PKCS12 structure. - - -.. py:method:: PKCS12.set_friendlyname(name) - - Replace or set the friendlyName portion of the PKCS12 structure. - - -.. py:method:: PKCS12.set_privatekey(pkey) - - Replace or set private key portion of the PKCS12 structure - +.. autoclass:: PKCS12 + :members: .. _openssl-509ext: X509Extension objects --------------------- -X509Extension objects have several methods: - -.. py:method:: X509Extension.get_critical() - - Return the critical field of the extension object. - - -.. py:method:: X509Extension.get_short_name() - - Retrieve the short descriptive name for this extension. - - The result is a byte string like :py:const:`basicConstraints`. - - .. versionadded:: 0.12 - - -.. py:method:: X509Extension.get_data() - - Retrieve the data for this extension. - - The result is the ASN.1 encoded form of the extension data as a byte string. - - .. versionadded:: 0.12 - +.. autoclass:: X509Extension + :members: + :special-members: + :exclude-members: __weakref__ .. _openssl-netscape-spki: NetscapeSPKI objects -------------------- -NetscapeSPKI objects have the following methods: - -.. py:method:: NetscapeSPKI.b64_encode() - - Return a base64-encoded string representation of the object. - - -.. py:method:: NetscapeSPKI.get_pubkey() - - Return the public key of object. - - -.. py:method:: NetscapeSPKI.set_pubkey(key) - - Set the public key of the object to *key*. - - -.. py:method:: NetscapeSPKI.sign(key, digest_name) - - Sign the NetscapeSPKI object using the given *key* and *digest_name*. - *digest_name* must be a string describing a digest algorithm supported by - OpenSSL (by EVP_get_digestbyname, specifically). For example, - :py:const:`"md5"` or :py:const:`"sha1"`. - - -.. py:method:: NetscapeSPKI.verify(key) - - Verify the NetscapeSPKI object using the given *key*. - +.. autoclass:: NetscapeSPKI + :members: + :special-members: + :exclude-members: __weakref__ .. _crl: CRL objects ----------- -CRL objects have the following methods: - -.. py:method:: CRL.add_revoked(revoked) - - Add a Revoked object to the CRL, by value not reference. - - -.. py:method:: CRL.export(cert, key[, type=FILETYPE_PEM][, days=100][, digest=b'md5']) - - Use *cert* and *key* to sign the CRL and return the CRL as a string. - *days* is the number of days before the next CRL is due. - *digest* is the algorithm that will be used to sign CRL. - - -.. py:method:: CRL.get_revoked() - - Return a tuple of Revoked objects, by value not reference. - +.. autoclass:: CRL + :members: + :special-members: + :exclude-members: __weakref__ .. _revoked: Revoked objects --------------- -Revoked objects have the following methods: - -.. py:method:: Revoked.all_reasons() - - Return a list of all supported reasons. - - -.. py:method:: Revoked.get_reason() - - Return the revocation reason as a str. Can be - None, which differs from "Unspecified". - - -.. py:method:: Revoked.get_rev_date() +.. autoclass:: Revoked + :members: - Return the revocation date as a str. - The string is formatted as an ASN1 GENERALIZEDTIME. +Exceptions +---------- +.. py:exception:: Error -.. py:method:: Revoked.get_serial() - - Return a str containing a hex number of the serial of the revoked certificate. - - -.. py:method:: Revoked.set_reason(reason) - - Set the revocation reason. *reason* must be None or a string, but the - values are limited. Spaces and case are ignored. See - :py:meth:`all_reasons`. - - -.. py:method:: Revoked.set_rev_date(date) - - Set the revocation date. - The string is formatted as an ASN1 GENERALIZEDTIME. - + Generic exception used in the :py:mod:`.crypto` module. -.. py:method:: Revoked.set_serial(serial) +Digest names +------------ - *serial* is a string containing a hex number of the serial of the revoked certificate. +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, +:py:const:`b"md5"` or :py:const:`b"sha1"`. + +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/crypto/EVP_DigestInit.html + +Backwards compatible type names +------------------------------- + +When PyOpenSSL was originally written, the most current version of +Python was 2.1. It made a distinction between classes and types. None +of the versions of Python currently supported by PyOpenSSL still +enforce that distinction: the type of an instance of an +:py:class:`X509` object is now simply :py:class:`X509`. Originally, +the type would have been :py:class:`X509Type`. These days, +:py:class:`X509Type` and :py:class:`X509` are literally the same +object. PyOpenSSL maintains these old names for backwards +compatibility. + +Here's a table of these backwards-compatible names: + +========================= ============================= +Type name Backwards-compatible name +========================= ============================= +:py:class:`X509` :py:class:`X509Type` +:py:class:`X509Name` :py:class:`X509NameType` +:py:class:`X509Req` :py:class:`X509ReqType` +:py:class:`X509Store` :py:class:`X509StoreType` +:py:class:`X509Extension` :py:class:`X509ExtensionType` +:py:class:`PKey` :py:class:`PKeyType` +:py:class:`PKCS7` :py:class:`PKCS7Type` +:py:class:`PKCS12` :py:class:`PKCS12Type` +:py:class:`NetscapeSPKI` :py:class:`NetscapeSPKIType` +:py:class:`CRL` :py:class:`CRLType` +========================= ============================= + +Some objects, such as :py:class`Revoked`, don't have ``Type`` +equivalents, because they were added after the restriction had been +lifted. diff --git a/doc/conf.py b/doc/conf.py index b13925f..dd6a334 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -11,7 +11,33 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +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 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, ".."))) @@ -28,7 +54,7 @@ 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 = [] +extensions = ["sphinx.ext.autodoc"] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -44,14 +70,15 @@ master_doc = 'index' # General information about the project. project = u'pyOpenSSL' -copyright = u'2011, Jean-Paul Calderone' +authors = u"The pyOpenSSL developers" +copyright = u"2001-{0}, {1}".format(datetime.date.today().year, 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 = '0.15.1' +version = find_version("../OpenSSL/version.py") # The full version, including alpha/beta/rc tags. release = version @@ -182,7 +209,7 @@ htmlhelp_basename = 'pyOpenSSLdoc' # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'pyOpenSSL.tex', u'pyOpenSSL Documentation', - u'Jean-Paul Calderone', 'manual'), + authors, 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -215,5 +242,5 @@ latex_documents = [ # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'pyopenssl', u'pyOpenSSL Documentation', - [u'Jean-Paul Calderone'], 1) + [authors], 1) ] diff --git a/examples/certgen.py b/examples/certgen.py index f157235..53c19b1 100644 --- a/examples/certgen.py +++ b/examples/certgen.py @@ -25,12 +25,12 @@ def createKeyPair(type, bits): 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 @@ -45,14 +45,14 @@ def createCertRequest(pkey, digest="md5", **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. @@ -64,9 +64,11 @@ def createCertificate(req, (issuerCert, issuerKey), serial, (notBefore, notAfter 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) diff --git a/examples/mk_simple_certs.py b/examples/mk_simple_certs.py index 9dfdd2e..7129f13 100644 --- a/examples/mk_simple_certs.py +++ b/examples/mk_simple_certs.py @@ -4,14 +4,25 @@ Create certificates and private keys for the 'simple' example. from OpenSSL import crypto from certgen import * # yes yes, I know, I'm lazy -cakey = createKeyPair(TYPE_RSA, 1024) +cakey = createKeyPair(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)) + +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(TYPE_RSA, 1024) + pkey = createKeyPair(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)) + 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')) + diff --git a/examples/simple/client.py b/examples/simple/client.py index 0247c67..36c37cd 100644 --- a/examples/simple/client.py +++ b/examples/simple/client.py @@ -8,16 +8,17 @@ Simple SSL client, using blocking I/O """ -from OpenSSL import SSL +from OpenSSL import SSL, crypto import sys, os, select, socket 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]) @@ -41,10 +42,10 @@ while 1: 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 diff --git a/examples/simple/server.py b/examples/simple/server.py index 37e36dd..df7c5a4 100644 --- a/examples/simple/server.py +++ b/examples/simple/server.py @@ -8,17 +8,18 @@ Simple echo server, using nonblocking I/O """ -from OpenSSL import SSL +from OpenSSL import SSL, crypto import sys, os, select, socket 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]) @@ -44,12 +45,12 @@ 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() @@ -57,27 +58,27 @@ def dropClient(cli, errors=None): while 1: try: - r,w,_ = select.select([server]+clients.keys(), writers.keys(), []) + r, w, _ = select.select([server] + list(clients.keys()), list(writers.keys()), []) except: break for cli in r: if cli == server: cli,addr = server.accept() - print 'Connection from %s' % (addr,) + print('Connection from %s' % (addr,)) clients[cli] = addr else: try: - ret = cli.recv(1024) + 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 @@ -88,7 +89,7 @@ while 1: pass except SSL.ZeroReturnError: dropClient(cli) - except SSL.Error, errors: + except SSL.Error as errors: dropClient(cli, errors) else: writers[cli] = writers[cli][ret:] @@ -5,17 +5,45 @@ # """ -Installation script for the OpenSSL module +Installation script for the OpenSSL module. """ +import codecs +import os +import re import sys from setuptools import setup from setuptools.command.test import test as TestCommand -# XXX Deduplicate this -__version__ = '0.15.1' +HERE = os.path.abspath(os.path.dirname(__file__)) +META_PATH = os.path.join("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)) class PyTest(TestCommand): @@ -38,38 +66,18 @@ class PyTest(TestCommand): sys.exit(errno) -setup(name='pyOpenSSL', version=__version__, - packages = ['OpenSSL'], - package_dir = {'OpenSSL': 'OpenSSL'}, - py_modules = ['OpenSSL.__init__', - 'OpenSSL.tsafe', - 'OpenSSL.rand', - 'OpenSSL.crypto', - 'OpenSSL.SSL', - 'OpenSSL.version', - 'OpenSSL.test.__init__', - 'OpenSSL.test.util', - 'OpenSSL.test.test_crypto', - 'OpenSSL.test.test_rand', - 'OpenSSL.test.test_ssl', - 'OpenSSL.test.test_tsafe', - 'OpenSSL.test.test_util',], - description = 'Python wrapper module around the OpenSSL library', - author = 'Jean-Paul Calderone', - author_email = 'exarkun@twistedmatrix.com', - maintainer = 'Jean-Paul Calderone', - maintainer_email = 'exarkun@twistedmatrix.com', - url = 'https://github.com/pyca/pyopenssl', - license = 'APL2', - install_requires=["cryptography>=0.7", "six>=1.5.2"], - 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 ;)""", - classifiers = [ +setup( + name=find_meta("title"), + version=find_meta("version"), + description=find_meta("summary"), + long_description=read_file("README.rst"), + author=find_meta("author"), + author_email=find_meta("email"), + maintainer="Hynek Schlawack", + maintainer_email="hs@ox.cx", + url=find_meta("uri"), + license=find_meta("license"), + classifiers=[ 'Development Status :: 6 - Mature', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', @@ -77,20 +85,10 @@ High-level wrapper around a subset of the OpenSSL library, includes 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX', - # General classifiers to indicate "this project supports Python 2" and - # "this project supports Python 3". 'Programming Language :: Python :: 2', - # In particular, this makes pyOpenSSL show up on - # https://pypi.python.org/pypi?:action=browse&c=533&show=all and is in - # accordance with - # http://docs.python.org/2/howto/pyporting.html#universal-bits-of-advice - 'Programming Language :: Python :: 3', - - # More specific classifiers to indicate more precisely which versions - # of those languages the project supports. 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.2', + 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: Implementation :: CPython', @@ -98,11 +96,32 @@ High-level wrapper around a subset of the OpenSSL library, includes 'Topic :: Security :: Cryptography', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: System :: Networking', - ], - test_suite="OpenSSL", - tests_require=[ - "pytest", - ], - cmdclass={ - "test": PyTest, - }) + ], + + packages=['OpenSSL'], + package_dir={'OpenSSL': 'OpenSSL'}, + py_modules=['OpenSSL.__init__', + 'OpenSSL.tsafe', + 'OpenSSL.rand', + 'OpenSSL.crypto', + 'OpenSSL.SSL', + 'OpenSSL.version', + 'OpenSSL.test.__init__', + 'OpenSSL.test.util', + 'OpenSSL.test.test_crypto', + 'OpenSSL.test.test_rand', + 'OpenSSL.test.test_ssl', + 'OpenSSL.test.test_tsafe', + 'OpenSSL.test.test_util',], + install_requires=[ + "cryptography>=0.7", + "six>=1.5.2" + ], + test_suite="OpenSSL", + tests_require=[ + "pytest", + ], + cmdclass={ + "test": PyTest, + } +) @@ -1,16 +1,18 @@ [tox] -envlist = pypy,py26,py27,py32,py33,py34,meta +envlist = {pypy,py26,py27,py33,py34}{,-cryptographyMaster},pypi-readme,check-manifest,meta [testenv] deps = setuptools>=7.0 # older setuptools pollute CWD with egg files of dependencies coverage + cryptographyMaster: git+https://github.com/pyca/cryptography.git setenv = # Do not allowed the executing environment to pollute the test environment # with extra packages. PYTHONPATH= commands = python -c "import OpenSSL.SSL; print(OpenSSL.SSL.SSLeay_version(OpenSSL.SSL.SSLEAY_VERSION))" + python -c "import cryptography; print(cryptography.__version__)" coverage run --branch --source=OpenSSL setup.py test coverage report -m @@ -26,3 +28,14 @@ commands = pip-review pyroma -d . +[testenv:pypi-readme] +deps = + readme +commands = + python setup.py check -r -s + +[testenv:check-manifest] +deps = + check-manifest +commands = + check-manifest |