Split up the one big CRL test and deprecate the default message digest.
diff --git a/OpenSSL/crypto.py b/OpenSSL/crypto.py
index 95ccc18..9f44b44 100644
--- a/OpenSSL/crypto.py
+++ b/OpenSSL/crypto.py
@@ -2,6 +2,7 @@
from base64 import b16encode
from functools import partial
from operator import __eq__, __ne__, __lt__, __le__, __gt__, __ge__
+from warnings import warn as _warn
from six import (
integer_types as _integer_types,
@@ -24,6 +25,10 @@
TYPE_RSA = _lib.EVP_PKEY_RSA
TYPE_DSA = _lib.EVP_PKEY_DSA
+# A marker object to observe whether some optional arguments are passed any
+# value or not.
+_undefined = object()
+
class Error(Exception):
"""
@@ -1707,7 +1712,8 @@
_raise_current_error()
- def export(self, cert, key, type=FILETYPE_PEM, days=100, digest="md5"):
+ def export(self, cert, key, type=FILETYPE_PEM, days=100,
+ digest=_undefined):
"""
export a CRL as a string
@@ -1722,7 +1728,7 @@
:param days: The number of days until the next update of this CRL.
:type days: :py:data:`int`
- :param digest: The message digest to use
+ :param digest: The message digest to use (eg ``"sha1"``).
:type digest: :py:data:`str`
:return: :py:data:`str`
@@ -1734,6 +1740,15 @@
if not isinstance(type, int):
raise TypeError("type must be an integer")
+ if digest is _undefined:
+ _warn(
+ "The default message digest (md5) is deprecated. "
+ "Pass the name of a message digest explicitly.",
+ category=DeprecationWarning,
+ stacklevel=2,
+ )
+ digest = "md5"
+
digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
if digest_obj == _ffi.NULL:
raise ValueError("No such digest method")
diff --git a/OpenSSL/test/test_crypto.py b/OpenSSL/test/test_crypto.py
index 5c4eac4..b2ad004 100644
--- a/OpenSSL/test/test_crypto.py
+++ b/OpenSSL/test/test_crypto.py
@@ -6,6 +6,7 @@
"""
from unittest import main
+from warnings import catch_warnings, simplefilter
import base64
import os
@@ -2925,11 +2926,9 @@
self.assertRaises(TypeError, CRL, None)
- def test_export(self):
+ def _get_crl(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.
+ Get a new ``CRL`` with a revocation.
"""
crl = CRL()
revoked = Revoked()
@@ -2938,32 +2937,92 @@
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.
+ """
+ crl = self._get_crl()
# PEM format
dumped_crl = crl.export(self.cert, self.pkey, days=20)
text = _runopenssl(dumped_crl, b"crl", b"-noout", b"-text")
+
+ # These magic values are based on the way the CRL above was constructed
+ # and with what certificate it was exported.
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.index(
+ b('Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=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 = crl.export(self.cert, self.pkey, FILETYPE_ASN1)
- text = _runopenssl(dumped_crl, b"crl", b"-noout", b"-text", b"-inform", b"DER")
+ text = _runopenssl(
+ dumped_crl, b"crl", b"-noout", b"-text", b"-inform", b"DER"
+ )
text.index(b('Serial Number: 03AB'))
text.index(b('Superseded'))
- text.index(b('Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=Testing Root CA'))
+ text.index(
+ b('Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=Testing Root CA')
+ )
+
+
+ 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)
+ text = _runopenssl(
+ dumped_crl, b"crl", b"-noout", b"-text", b"-inform", b"DER"
+ )
# text format
dumped_text = crl.export(self.cert, self.pkey, type=FILETYPE_TEXT)
self.assertEqual(text, dumped_text)
- # sha1 digest
+
+ 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='sha1')
text = _runopenssl(dumped_crl, b"crl", b"-noout", b"-text")
text.index(b('Signature Algorithm: sha1'))
- # md5 digest(by default)
- dumped_crl = crl.export(self.cert, self.pkey)
+
+ def test_export_default_digest(self):
+ """
+ If not passed the name of a digest function, ``CRL.export`` uses a
+ signature algorithm based on MD5 and emits a deprecation warning.
+ """
+ crl = self._get_crl()
+ with catch_warnings(record=True) as catcher:
+ simplefilter("always")
+ dumped_crl = crl.export(self.cert, self.pkey)
+ self.assertEqual(
+ "The default message digest (md5) is deprecated. "
+ "Pass the name of a message digest explicitly.",
+ str(catcher[0].message),
+ )
text = _runopenssl(dumped_crl, b"crl", b"-noout", b"-text")
text.index(b('Signature Algorithm: md5'))