diff options
| author | Robie Basak <robie@justgohome.co.uk> | 2017-02-24 21:19:33 (GMT) |
|---|---|---|
| committer | Robie Basak <robie@justgohome.co.uk> | 2017-02-24 21:19:33 (GMT) |
| commit | 2de942f7a60d7a04900eedacbf68d8e736fd9b1a (patch) | |
| tree | b1d92ccc8e83bc24c7f99e973f0e3da26c4401dc | |
| parent | c23f259317dfbae96b2ad9cc3d9ee06a489a1f4c (diff) | |
| -rw-r--r-- | PKG-INFO | 2 | ||||
| -rw-r--r-- | acme.egg-info/PKG-INFO | 2 | ||||
| -rw-r--r-- | acme.egg-info/SOURCES.txt | 2 | ||||
| -rw-r--r-- | acme.egg-info/requires.txt | 3 | ||||
| -rw-r--r-- | acme/challenges.py | 76 | ||||
| -rw-r--r-- | acme/challenges_test.py | 103 | ||||
| -rw-r--r-- | acme/client.py | 9 | ||||
| -rw-r--r-- | acme/client_test.py | 6 | ||||
| -rw-r--r-- | acme/dns_resolver.py | 45 | ||||
| -rw-r--r-- | acme/dns_resolver_test.py | 77 | ||||
| -rw-r--r-- | acme/errors.py | 6 | ||||
| -rw-r--r-- | acme/fields.py | 2 | ||||
| -rw-r--r-- | acme/messages.py | 2 | ||||
| -rw-r--r-- | acme/test_util.py | 38 | ||||
| -rw-r--r-- | acme/util.py | 18 | ||||
| -rw-r--r-- | acme/util_test.py | 18 | ||||
| -rw-r--r-- | debian/changelog | 24 | ||||
| -rw-r--r-- | debian/control | 4 | ||||
| -rw-r--r-- | debian/patches/fix-tests-under-openssl-1-1.patch | 245 | ||||
| -rw-r--r-- | debian/patches/series | 1 | ||||
| -rwxr-xr-x | debian/rules | 5 | ||||
| -rw-r--r--[l---------] | examples/standalone/localhost/cert.pem | 14 | ||||
| -rw-r--r--[l---------] | examples/standalone/localhost/key.pem | 10 | ||||
| -rw-r--r-- | setup.py | 8 |
24 files changed, 693 insertions, 27 deletions
@@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: acme -Version: 0.8.1 +Version: 0.9.3 Summary: ACME protocol implementation in Python Home-page: https://github.com/letsencrypt/letsencrypt Author: Certbot Project diff --git a/acme.egg-info/PKG-INFO b/acme.egg-info/PKG-INFO index 06ac790..d917980 100644 --- a/acme.egg-info/PKG-INFO +++ b/acme.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: acme -Version: 0.8.1 +Version: 0.9.3 Summary: ACME protocol implementation in Python Home-page: https://github.com/letsencrypt/letsencrypt Author: Certbot Project diff --git a/acme.egg-info/SOURCES.txt b/acme.egg-info/SOURCES.txt index f292271..b74c211 100644 --- a/acme.egg-info/SOURCES.txt +++ b/acme.egg-info/SOURCES.txt @@ -10,6 +10,8 @@ acme/client.py acme/client_test.py acme/crypto_util.py acme/crypto_util_test.py +acme/dns_resolver.py +acme/dns_resolver_test.py acme/errors.py acme/errors_test.py acme/fields.py diff --git a/acme.egg-info/requires.txt b/acme.egg-info/requires.txt index 1dae1e1..36b0fba 100644 --- a/acme.egg-info/requires.txt +++ b/acme.egg-info/requires.txt @@ -14,6 +14,9 @@ nose pep8 tox +[dns] +dnspython>=1.12 + [docs] Sphinx>=1.0 sphinx_rtd_theme diff --git a/acme/challenges.py b/acme/challenges.py index c436cc6..9f9cc05 100644 --- a/acme/challenges.py +++ b/acme/challenges.py @@ -9,12 +9,12 @@ from cryptography.hazmat.primitives import hashes import OpenSSL import requests +from acme import dns_resolver from acme import errors from acme import crypto_util from acme import fields from acme import jose - logger = logging.getLogger(__name__) @@ -207,6 +207,74 @@ class KeyAuthorizationChallenge(_TokenChallenge): @ChallengeResponse.register +class DNS01Response(KeyAuthorizationChallengeResponse): + """ACME dns-01 challenge response.""" + typ = "dns-01" + + def simple_verify(self, chall, domain, account_public_key): + """Simple verify. + + :param challenges.DNS01 chall: Corresponding challenge. + :param unicode domain: Domain name being verified. + :param JWK account_public_key: Public key for the key pair + being authorized. + + :returns: ``True`` iff validation with the TXT records resolved from a + DNS server is successful. + :rtype: bool + + """ + if not self.verify(chall, account_public_key): + logger.debug("Verification of key authorization in response failed") + return False + + validation_domain_name = chall.validation_domain_name(domain) + validation = chall.validation(account_public_key) + logger.debug("Verifying %s at %s...", chall.typ, validation_domain_name) + + try: + txt_records = dns_resolver.txt_records_for_name( + validation_domain_name) + except errors.DependencyError: + raise errors.DependencyError("Local validation for 'dns-01' " + "challenges requires 'dnspython'") + exists = validation in txt_records + if not exists: + logger.debug("Key authorization from response (%r) doesn't match " + "any DNS response in %r", self.key_authorization, + txt_records) + return exists + + +@Challenge.register # pylint: disable=too-many-ancestors +class DNS01(KeyAuthorizationChallenge): + """ACME dns-01 challenge.""" + response_cls = DNS01Response + typ = response_cls.typ + + LABEL = "_acme-challenge" + """Label clients prepend to the domain name being validated.""" + + def validation(self, account_key, **unused_kwargs): + """Generate validation. + + :param JWK account_key: + :rtype: unicode + + """ + return jose.b64encode(hashlib.sha256(self.key_authorization( + account_key).encode("utf-8")).digest()).decode() + + def validation_domain_name(self, name): + """Domain name for TXT validation record. + + :param unicode name: Domain name being validated. + + """ + return "{0}.{1}".format(self.LABEL, name) + + +@ChallengeResponse.register class HTTP01Response(KeyAuthorizationChallengeResponse): """ACME http-01 challenge response.""" typ = "http-01" @@ -231,8 +299,8 @@ class HTTP01Response(KeyAuthorizationChallengeResponse): being authorized. :param int port: Port used in the validation. - :returns: ``True`` iff validation is successful, ``False`` - otherwise. + :returns: ``True`` iff validation with the files currently served by the + HTTP server is successful. :rtype: bool """ @@ -410,7 +478,7 @@ class TLSSNI01Response(KeyAuthorizationChallengeResponse): :returns: ``True`` iff client's control of the domain has been - verified, ``False`` otherwise. + verified. :rtype: bool """ diff --git a/acme/challenges_test.py b/acme/challenges_test.py index 04b7442..5ac07ab 100644 --- a/acme/challenges_test.py +++ b/acme/challenges_test.py @@ -10,7 +10,7 @@ from six.moves.urllib import parse as urllib_parse # pylint: disable=import-err from acme import errors from acme import jose from acme import test_util - +from acme.dns_resolver import DNS_REQUIREMENT CERT = test_util.load_comparable_cert('cert.pem') KEY = jose.JWKRSA(key=test_util.load_rsa_private_key('rsa512_key.pem')) @@ -77,6 +77,107 @@ class KeyAuthorizationChallengeResponseTest(unittest.TestCase): self.assertFalse(response.verify(self.chall, KEY.public_key())) +class DNS01ResponseTest(unittest.TestCase): + # pylint: disable=too-many-instance-attributes + + def setUp(self): + from acme.challenges import DNS01Response + self.msg = DNS01Response(key_authorization=u'foo') + self.jmsg = { + 'resource': 'challenge', + 'type': 'dns-01', + 'keyAuthorization': u'foo', + } + + from acme.challenges import DNS01 + self.chall = DNS01(token=(b'x' * 16)) + self.response = self.chall.response(KEY) + self.records_for_name_path = "acme.dns_resolver.txt_records_for_name" + + def test_to_partial_json(self): + self.assertEqual(self.jmsg, self.msg.to_partial_json()) + + def test_from_json(self): + from acme.challenges import DNS01Response + self.assertEqual(self.msg, DNS01Response.from_json(self.jmsg)) + + def test_from_json_hashable(self): + from acme.challenges import DNS01Response + hash(DNS01Response.from_json(self.jmsg)) + + def test_simple_verify_bad_key_authorization(self): + key2 = jose.JWKRSA.load(test_util.load_vector('rsa256_key.pem')) + self.response.simple_verify(self.chall, "local", key2.public_key()) + + @mock.patch('acme.dns_resolver.DNS_AVAILABLE', False) + def test_simple_verify_without_dns(self): + self.assertRaises( + errors.DependencyError, self.response.simple_verify, + self.chall, 'local', KEY.public_key()) + + @test_util.skip_unless(test_util.requirement_available(DNS_REQUIREMENT), + "optional dependency dnspython is not available") + def test_simple_verify_good_validation(self): # pragma: no cover + with mock.patch(self.records_for_name_path) as mock_resolver: + mock_resolver.return_value = [ + self.chall.validation(KEY.public_key())] + self.assertTrue(self.response.simple_verify( + self.chall, "local", KEY.public_key())) + mock_resolver.assert_called_once_with( + self.chall.validation_domain_name("local")) + + @test_util.skip_unless(test_util.requirement_available(DNS_REQUIREMENT), + "optional dependency dnspython is not available") + def test_simple_verify_good_validation_multitxts(self): # pragma: no cover + with mock.patch(self.records_for_name_path) as mock_resolver: + mock_resolver.return_value = [ + "!", self.chall.validation(KEY.public_key())] + self.assertTrue(self.response.simple_verify( + self.chall, "local", KEY.public_key())) + mock_resolver.assert_called_once_with( + self.chall.validation_domain_name("local")) + + @test_util.skip_unless(test_util.requirement_available(DNS_REQUIREMENT), + "optional dependency dnspython is not available") + def test_simple_verify_bad_validation(self): # pragma: no cover + with mock.patch(self.records_for_name_path) as mock_resolver: + mock_resolver.return_value = ["!"] + self.assertFalse(self.response.simple_verify( + self.chall, "local", KEY.public_key())) + + +class DNS01Test(unittest.TestCase): + + def setUp(self): + from acme.challenges import DNS01 + self.msg = DNS01(token=jose.decode_b64jose( + 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA')) + self.jmsg = { + 'type': 'dns-01', + 'token': 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA', + } + + def test_validation_domain_name(self): + self.assertEqual('_acme-challenge.www.example.com', + self.msg.validation_domain_name('www.example.com')) + + def test_validation(self): + self.assertEqual( + "rAa7iIg4K2y63fvUhCfy8dP1Xl7wEhmQq0oChTcE3Zk", + self.msg.validation(KEY)) + + def test_to_partial_json(self): + self.assertEqual(self.jmsg, self.msg.to_partial_json()) + + def test_from_json(self): + from acme.challenges import DNS01 + self.assertEqual(self.msg, DNS01.from_json(self.jmsg)) + + def test_from_json_hashable(self): + from acme.challenges import DNS01 + hash(DNS01.from_json(self.jmsg)) + + class HTTP01ResponseTest(unittest.TestCase): # pylint: disable=too-many-instance-attributes diff --git a/acme/client.py b/acme/client.py index 117ee6b..de7eef2 100644 --- a/acme/client.py +++ b/acme/client.py @@ -89,8 +89,6 @@ class Client(object): # pylint: disable=too-many-instance-attributes :returns: Registration Resource. :rtype: `.RegistrationResource` - :raises .UnexpectedUpdate: - """ new_reg = messages.NewRegistration() if new_reg is None else new_reg assert isinstance(new_reg, messages.NewRegistration) @@ -101,12 +99,7 @@ class Client(object): # pylint: disable=too-many-instance-attributes # "Instance of 'Field' has no key/contact member" bug: # pylint: disable=no-member - regr = self._regr_from_response(response) - if (regr.body.key != self.key.public_key() or - regr.body.contact != new_reg.contact): - raise errors.UnexpectedUpdate(regr) - - return regr + return self._regr_from_response(response) def _send_recv_regr(self, regr, body): response = self.net.post(regr.uri, body) diff --git a/acme/client_test.py b/acme/client_test.py index a526a09..585576e 100644 --- a/acme/client_test.py +++ b/acme/client_test.py @@ -102,12 +102,6 @@ class ClientTest(unittest.TestCase): self.assertEqual(self.regr, self.client.register(self.new_reg)) # TODO: test POST call arguments - # TODO: split here and separate test - reg_wrong_key = self.regr.body.update(key=KEY2.public_key()) - self.response.json.return_value = reg_wrong_key.to_json() - self.assertRaises( - errors.UnexpectedUpdate, self.client.register, self.new_reg) - def test_register_missing_next(self): self.response.status_code = http_client.CREATED self.assertRaises( diff --git a/acme/dns_resolver.py b/acme/dns_resolver.py new file mode 100644 index 0000000..2677d92 --- /dev/null +++ b/acme/dns_resolver.py @@ -0,0 +1,45 @@ +"""DNS Resolver for ACME client. +Required only for local validation of 'dns-01' challenges. +""" +import logging + +from acme import errors +from acme import util + +DNS_REQUIREMENT = 'dnspython>=1.12' + +try: + util.activate(DNS_REQUIREMENT) + # pragma: no cover + import dns.exception + import dns.resolver + DNS_AVAILABLE = True +except errors.DependencyError: # pragma: no cover + DNS_AVAILABLE = False + + +logger = logging.getLogger(__name__) + + +def txt_records_for_name(name): + """Resolve the name and return the TXT records. + + :param unicode name: Domain name being verified. + + :returns: A list of txt records, if empty the name could not be resolved + :rtype: list of unicode + + """ + if not DNS_AVAILABLE: + raise errors.DependencyError( + '{0} is required to use this function'.format(DNS_REQUIREMENT)) + try: + dns_response = dns.resolver.query(name, 'TXT') + except dns.resolver.NXDOMAIN as error: + return [] + except dns.exception.DNSException as error: + logger.error("Error resolving %s: %s", name, str(error)) + return [] + + return [txt_rec.decode("utf-8") for rdata in dns_response + for txt_rec in rdata.strings] diff --git a/acme/dns_resolver_test.py b/acme/dns_resolver_test.py new file mode 100644 index 0000000..2e2edd0 --- /dev/null +++ b/acme/dns_resolver_test.py @@ -0,0 +1,77 @@ +"""Tests for acme.dns_resolver.""" +import unittest + +import mock +from six.moves import reload_module # pylint: disable=import-error + +from acme import errors +from acme import test_util +from acme.dns_resolver import DNS_REQUIREMENT + + +if test_util.requirement_available(DNS_REQUIREMENT): + import dns + + +def create_txt_response(name, txt_records): + """ + Returns an RRSet containing the 'txt_records' as the result of a DNS + query for 'name'. + + This takes advantage of the fact that an Answer object mostly behaves + like an RRset. + """ + return dns.rrset.from_text_list(name, 60, "IN", "TXT", txt_records) + + +class TxtRecordsForNameTest(unittest.TestCase): + """Tests for acme.dns_resolver.txt_records_for_name.""" + @classmethod + def _call(cls, *args, **kwargs): + from acme.dns_resolver import txt_records_for_name + return txt_records_for_name(*args, **kwargs) + + +@test_util.skip_unless(test_util.requirement_available(DNS_REQUIREMENT), + "optional dependency dnspython is not available") +class TxtRecordsForNameWithDnsTest(TxtRecordsForNameTest): + """Tests for acme.dns_resolver.txt_records_for_name with dns.""" + @mock.patch("acme.dns_resolver.dns.resolver.query") + def test_txt_records_for_name_with_single_response(self, mock_dns): + mock_dns.return_value = create_txt_response('name', ['response']) + self.assertEqual(['response'], self._call('name')) + + @mock.patch("acme.dns_resolver.dns.resolver.query") + def test_txt_records_for_name_with_multiple_responses(self, mock_dns): + mock_dns.return_value = create_txt_response( + 'name', ['response1', 'response2']) + self.assertEqual(['response1', 'response2'], self._call('name')) + + @mock.patch("acme.dns_resolver.dns.resolver.query") + def test_txt_records_for_name_domain_not_found(self, mock_dns): + mock_dns.side_effect = dns.resolver.NXDOMAIN + self.assertEquals([], self._call('name')) + + @mock.patch("acme.dns_resolver.dns.resolver.query") + def test_txt_records_for_name_domain_other_error(self, mock_dns): + mock_dns.side_effect = dns.exception.DNSException + self.assertEquals([], self._call('name')) + + +class TxtRecordsForNameWithoutDnsTest(TxtRecordsForNameTest): + """Tests for acme.dns_resolver.txt_records_for_name without dns.""" + def setUp(self): + from acme import dns_resolver + dns_resolver.DNS_AVAILABLE = False + + def tearDown(self): + from acme import dns_resolver + reload_module(dns_resolver) + + def test_exception_raised(self): + self.assertRaises( + errors.DependencyError, self._call, "example.org") + + +if __name__ == '__main__': + unittest.main() # pragma: no cover diff --git a/acme/errors.py b/acme/errors.py index 77d47c5..7446b60 100644 --- a/acme/errors.py +++ b/acme/errors.py @@ -6,6 +6,10 @@ class Error(Exception): """Generic ACME error.""" +class DependencyError(Error): + """Dependency error""" + + class SchemaValidationError(jose_errors.DeserializationError): """JSON schema ACME object validation error.""" @@ -49,7 +53,7 @@ class MissingNonce(NonceError): def __str__(self): return ('Server {0} response did not include a replay ' - 'nonce, headers: {1}'.format( + 'nonce, headers: {1} (This may be a service outage)'.format( self.response.request.method, self.response.headers)) diff --git a/acme/fields.py b/acme/fields.py index 002240b..12d09ac 100644 --- a/acme/fields.py +++ b/acme/fields.py @@ -24,7 +24,7 @@ class Fixed(jose.Field): def encode(self, value): if value != self.value: - logger.warn( + logger.warning( 'Overriding fixed field (%s) with %r', self.json_name, value) return value diff --git a/acme/messages.py b/acme/messages.py index 56bcb1d..563f806 100644 --- a/acme/messages.py +++ b/acme/messages.py @@ -27,6 +27,8 @@ class Error(jose.JSONObjectWithFields, errors.Error): ('dnssec', 'The server could not validate a DNSSEC signed domain'), ('invalidEmail', 'The provided email for a registration was invalid'), + ('invalidContact', + 'The provided contact URI was invalid'), ('malformed', 'The request message was malformed'), ('rateLimited', 'There were too many requests of a given type'), ('serverInternal', 'The server experienced an internal error'), diff --git a/acme/test_util.py b/acme/test_util.py index 24eceff..ba96851 100644 --- a/acme/test_util.py +++ b/acme/test_util.py @@ -5,12 +5,15 @@ """ import os import pkg_resources +import unittest from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization import OpenSSL +from acme import errors from acme import jose +from acme import util def vector_path(*names): @@ -73,3 +76,38 @@ def load_pyopenssl_private_key(*names): loader = _guess_loader( names[-1], OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_ASN1) return OpenSSL.crypto.load_privatekey(loader, load_vector(*names)) + + +def requirement_available(requirement): + """Checks if requirement can be imported. + + :rtype: bool + :returns: ``True`` iff requirement can be imported + + """ + try: + util.activate(requirement) + except errors.DependencyError: # pragma: no cover + return False + return True # pragma: no cover + + +def skip_unless(condition, reason): # pragma: no cover + """Skip tests unless a condition holds. + + This implements the basic functionality of unittest.skipUnless + which is only available on Python 2.7+. + + :param bool condition: If ``False``, the test will be skipped + :param str reason: the reason for skipping the test + + :rtype: callable + :returns: decorator that hides tests unless condition is ``True`` + + """ + if hasattr(unittest, "skipUnless"): + return unittest.skipUnless(condition, reason) + elif condition: + return lambda cls: cls + else: + return lambda cls: None diff --git a/acme/util.py b/acme/util.py index 1fff89a..ac445b2 100644 --- a/acme/util.py +++ b/acme/util.py @@ -1,7 +1,25 @@ """ACME utilities.""" +import pkg_resources import six +from acme import errors + def map_keys(dikt, func): """Map dictionary keys.""" return dict((func(key), value) for key, value in six.iteritems(dikt)) + + +def activate(requirement): + """Make requirement importable. + + :param str requirement: the distribution and version to activate + + :raises acme.errors.DependencyError: if cannot activate requirement + + """ + try: + for distro in pkg_resources.require(requirement): # pylint: disable=not-callable + distro.activate() + except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict): + raise errors.DependencyError('{0} is unavailable'.format(requirement)) diff --git a/acme/util_test.py b/acme/util_test.py index 00aa8b0..ba64654 100644 --- a/acme/util_test.py +++ b/acme/util_test.py @@ -1,6 +1,8 @@ """Tests for acme.util.""" import unittest +from acme import errors + class MapKeysTest(unittest.TestCase): """Tests for acme.util.map_keys.""" @@ -12,5 +14,21 @@ class MapKeysTest(unittest.TestCase): self.assertEqual({2: 2, 4: 4}, map_keys({1: 2, 3: 4}, lambda x: x + 1)) +class ActivateTest(unittest.TestCase): + """Tests for acme.util.activate.""" + + @classmethod + def _call(cls, *args, **kwargs): + from acme.util import activate + return activate(*args, **kwargs) + + def test_failure(self): + self.assertRaises(errors.DependencyError, self._call, 'acme>99.0.0') + + def test_success(self): + self._call('acme') + import acme as unused_acme + + if __name__ == '__main__': unittest.main() # pragma: no cover diff --git a/debian/changelog b/debian/changelog index 781a52f..ef2c8d7 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,27 @@ +python-acme (0.9.3-2~16.10.1) yakkety; urgency=medium + + * Backport new upstream stable release for Yakkety SRU. (LP: #1640978) + + -- Harlan Lieberman-Berg <hlieberman@debian.org> Wed, 14 Dec 2016 22:23:52 -0500 + +python-acme (0.9.3-2) unstable; urgency=medium + + * Add patch to fix tests with OpenSSL 1.1 (Closes: #844944) + + -- Harlan Lieberman-Berg <hlieberman@debian.org> Fri, 02 Dec 2016 17:53:38 -0500 + +python-acme (0.9.3-1) unstable; urgency=medium + + * New upstream release. + + -- Harlan Lieberman-Berg <hlieberman@debian.org> Thu, 13 Oct 2016 22:21:10 -0400 + +python-acme (0.8.1-2) unstable; urgency=medium + + * Ensure there's no network access at build time. + + -- Harlan Lieberman-Berg <hlieberman@debian.org> Fri, 02 Sep 2016 17:47:46 -0400 + python-acme (0.8.1-1) unstable; urgency=medium * New upstream release. diff --git a/debian/control b/debian/control index bdd474e..161face 100644 --- a/debian/control +++ b/debian/control @@ -8,6 +8,7 @@ Build-Depends: debhelper (>= 9~), dh-python, python-all (>= 2.6.6.3~), python-cryptography (>= 0.8), + python-dnspython (>= 1.12), python-docutils, python-mock, python-ndg-httpsclient, @@ -23,6 +24,7 @@ Build-Depends: debhelper (>= 9~), python-tz, python3, python3-cryptography (>= 0.8), + python3-dnspython (>= 1.12), python3-docutils, python3-mock, python3-ndg-httpsclient, @@ -46,6 +48,7 @@ Depends: ca-certificates, ${python:Depends} Breaks: python-letsencrypt (<< ${source:Upstream-Version}), python-certbot (<< ${source:Upstream-Version}) +Recommends: python-dnspython Suggests: python-acme-doc Description: ACME protocol library for Python 2 This is a library used by the Let's Encrypt client for the ACME @@ -62,6 +65,7 @@ Depends: ca-certificates, python3-openssl (>= 0.15), ${misc:Depends}, ${python3:Depends} +Recommends: python3-dnspython Suggests: python-acme-doc Description: ACME protocol library for Python 3 This is a library used by the Let's Encrypt client for the ACME diff --git a/debian/patches/fix-tests-under-openssl-1-1.patch b/debian/patches/fix-tests-under-openssl-1-1.patch new file mode 100644 index 0000000..69e3f42 --- /dev/null +++ b/debian/patches/fix-tests-under-openssl-1-1.patch @@ -0,0 +1,245 @@ +From 1599524c30b1528123f34165a1dd68dc2d618f09 Mon Sep 17 00:00:00 2001 +From: Peter Eckersley <pde@eff.org> +Date: Mon, 28 Nov 2016 16:41:07 -0800 +Subject: [PATCH 1/3] Ensure tests pass with openssl 1.1 + +A bunch of the acme.standalone and acme.crypto_util tests were using +weak crypto that is now prohibited :/ +--- + acme/crypto_util.py | 4 +++ + acme/crypto_util_test.py | 4 +-- + acme/standalone_test.py | 11 +++--- + acme/testdata/rsa2048_cert.pem | 22 ++++++++++++ + acme/testdata/rsa2048_key.pem | 55 +++++++++++++++-------------- + examples/standalone/localhost/cert.pem | 2 +- + examples/standalone/localhost/key.pem | 2 +- + 7 files changed, 64 insertions(+), 36 deletions(-) + create mode 100644 acme/testdata/rsa2048_cert.pem + +--- a/acme/crypto_util.py ++++ b/acme/crypto_util.py +@@ -63,6 +63,8 @@ + server_name) + return + new_context = OpenSSL.SSL.Context(self.method) ++ new_context.set_options(OpenSSL.SSL.OP_NO_SSLv2) ++ new_context.set_options(OpenSSL.SSL.OP_NO_SSLv3) + new_context.use_privatekey(key) + new_context.use_certificate(cert) + connection.set_context(new_context) +@@ -86,6 +88,8 @@ + sock, addr = self.sock.accept() + + context = OpenSSL.SSL.Context(self.method) ++ context.set_options(OpenSSL.SSL.OP_NO_SSLv2) ++ context.set_options(OpenSSL.SSL.OP_NO_SSLv3) + context.set_tlsext_servername_callback(self._pick_certificate_cb) + + ssl_sock = self.FakeConnection(OpenSSL.SSL.Connection(context, sock)) +--- a/acme/crypto_util_test.py ++++ b/acme/crypto_util_test.py +@@ -19,8 +19,8 @@ + """Tests for acme.crypto_util.SSLSocket/probe_sni.""" + + def setUp(self): +- self.cert = test_util.load_comparable_cert('cert.pem') +- key = test_util.load_pyopenssl_private_key('rsa512_key.pem') ++ self.cert = test_util.load_comparable_cert('rsa2048_cert.pem') ++ key = test_util.load_pyopenssl_private_key('rsa2048_key.pem') + # pylint: disable=protected-access + certs = {b'foo': (key, self.cert.wrapped)} + +--- a/acme/standalone_test.py ++++ b/acme/standalone_test.py +@@ -33,8 +33,8 @@ + + def setUp(self): + self.certs = {b'localhost': ( +- test_util.load_pyopenssl_private_key('rsa512_key.pem'), +- test_util.load_cert('cert.pem'), ++ test_util.load_pyopenssl_private_key('rsa2048_key.pem'), ++ test_util.load_cert('rsa2048_cert.pem'), + )} + from acme.standalone import TLSSNI01Server + self.server = TLSSNI01Server(("", 0), certs=self.certs) +@@ -114,8 +114,9 @@ + self.test_cwd = tempfile.mkdtemp() + localhost_dir = os.path.join(self.test_cwd, 'localhost') + os.makedirs(localhost_dir) +- shutil.copy(test_util.vector_path('cert.pem'), localhost_dir) +- shutil.copy(test_util.vector_path('rsa512_key.pem'), ++ shutil.copy(test_util.vector_path('rsa2048_cert.pem'), ++ os.path.join(localhost_dir, 'cert.pem')) ++ shutil.copy(test_util.vector_path('rsa2048_key.pem'), + os.path.join(localhost_dir, 'key.pem')) + + from acme.standalone import simple_tls_sni_01_server +@@ -147,7 +148,8 @@ + time.sleep(1) # wait until thread starts + else: + self.assertEqual(jose.ComparableX509(cert), +- test_util.load_comparable_cert('cert.pem')) ++ test_util.load_comparable_cert( ++ 'rsa2048_cert.pem')) + break + + +--- /dev/null ++++ b/acme/testdata/rsa2048_cert.pem +@@ -0,0 +1,22 @@ ++-----BEGIN CERTIFICATE----- ++MIIDjjCCAnagAwIBAgIJALVG/VbBb5U7MA0GCSqGSIb3DQEBCwUAMFsxCzAJBgNV ++BAYTAkFVMQswCQYDVQQIDAJXQTEeMBwGA1UEBwwVVGhlIG1pZGRsZSBvZiBub3do ++ZXJlMR8wHQYDVQQKDBZDZXJ0Ym90IFRlc3QgQ2VydHMgSW5jMCAXDTE2MTEyODIx ++MzUzN1oYDzIyOTAwOTEzMjEzNTM3WjBbMQswCQYDVQQGEwJBVTELMAkGA1UECAwC ++V0ExHjAcBgNVBAcMFVRoZSBtaWRkbGUgb2Ygbm93aGVyZTEfMB0GA1UECgwWQ2Vy ++dGJvdCBUZXN0IENlcnRzIEluYzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ++ggEBANoVT1pdvRUUBOqvm7M2ebLEHV7higUH7qAGUZEkfP6W4YriYVY+IHrH1svN ++PSa+oPTK7weDNmT11ehWnGyECIM9z2r2Hi9yVV0ycxh4hWQ4Nt8BAKZwCwaXpyWm ++7Gj6m2EzpSN5Dd67g5YAQBrUUh1+RRbFi9c0Ls/6ZOExMvfg8kqt4c2sXCgH1IFn ++xvvOjBYop7xh0x3L1Akyax0tw8qgQp/z5mkupmVDNJYPFmbzFPMNyDR61ed6QUTD ++g7P4UAuFkejLLzFvz5YaO7vC+huaTuPhInAhpzqpr4yU97KIjos2/83Itu/Cv8U1 ++RAeEeRTkh0WjUfltoem/5f8bIdsCAwEAAaNTMFEwHQYDVR0OBBYEFHy+bEYqwvFU ++uQLTkIfQ36AM2DQiMB8GA1UdIwQYMBaAFHy+bEYqwvFUuQLTkIfQ36AM2DQiMA8G ++A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAH3ANVzB59FcunZV/F8T ++RiCD6/gV7Jc3CswU8N8tVjzMCg2jOdTFF9iYZzNNKQvG13o/n5LkQr/lkKRQkWTx ++nkE5WZbR7vNqlzXgPa9NBiK5rPjgSt8azPW+Skct3Bj4B3PhTMSpoQ7PsUJ8UeV8 ++kTNR5xrRLt6/mLfRJTXWXBM43GEZi8lL5q0nqz0tPGISADshHMo6ZlUu5Hvfp5v+ ++aonpO4sVS9hGOVxjGNMXYApEUy4jid9jjAfEk6jeELJMbXGLy/botFgIJK/QPe6P ++AfbdFgtg/qzG7Uy0A1iXXfWdgwmVrhCoGYYWCn4yWCAm894QKtdim87CHSDP0WUf ++Esg= ++-----END CERTIFICATE----- +--- a/acme/testdata/rsa2048_key.pem ++++ b/acme/testdata/rsa2048_key.pem +@@ -1,27 +1,28 @@ +------BEGIN RSA PRIVATE KEY----- +-MIIEowIBAAKCAQEA8HwZMHeImB/iM8/n8CTCR4KeYQB2gLGO3v8xLms+PWH3Zbxc +-dVtEn25Y34scIh+iOuEXBcSBalBddLHKBGVN3nCfmpupoLm52xgRG44q9OWODpg4 +-FSi4afqVw2agMx0RHi0v3GVcdpqB83UW42kK1ESZHUuq7mxLg8u3IMYZFm6Amsf+ +-YQjBbDNn8NczJOFhsExP2EdM5ykgM1Om8aqTqqPMgPub68/r4Sym+BjLnvRq5Qtz +-h/jCfOBIIpAwg3lj7l8OyE3kkD3ALtuiuminNUqLHEkUaLq/Xiv8V8mvnrhG7h3Q +-+L1Xc707P0dz5YM5XxTMhmUE1cae/lQ0KbNrpwIDAQABAoIBAAiDXCDrGlrIRimv +-YnaN1pLRfOnSKl/D6VrbjdIm2b0yip9/W4aMBJHgRiUjt4s9s3CCJ1585lftIGHR +-KWWecHM/aWb/u7GE4Z9v6qsfDUY+GhlKKjIVjvGxfTu9lk446TI4R0l2DR/luFP2 +-ASlrvoZlJ0ZyN0rZapLv0zvFx32Tukd+3rcMmXfHl7aRGMZG1YTKNmBJ4d9iJ6cP +-HG3fgSzLQMPLNO/20MzbXdREG5FNQtwaMuFnIcVbtMCvc/71lQQEfANMLCUweEed +-YWGOjgDeh+731nJsopel+2TSTgnf5VhcFrgChZZdqeKvP+HbXjTE2VkWo7BrzoM7 +-xICYBwECgYEA/ZF/JOjZfwIMUdikv8vldJzRMdFryl4NJWnh4NeThNOEpUcpxnyK +-wyMnnQaGJa51u9EEnzl0sZ2h2ODjD6KFpz6fkWaVRq5SWalVPAoKZGaoPZV3IUOI +-8Tm0xkXho+A/FUUEcxCLME+3V9EdPfHaVRJOrbfDyxvNhsj4w9F0aAkCgYEA8sp7 +-XTrolOknJGv4Qt1w6gcm5+cMtLaRfi8ZHPHujl2x9eWE8/s2818az7jc0Xr/G4HQ +-NeU+3Es4BblEckSHmhUZhx26cZgkLSIIDofEtaEc6u8CyWfxsWvn3l4T3kMdeSLC +-9UoLk59AH2tkMIh8vzV8LSisLJa341lMdgryQi8CgYAlJKr7PSCe+i3Tz2hSsAts +-iYwbQBIKErzaPihYRzvUuSc1DreP26535y5mUg5UdrnISVXj/Qaa/fw3SLn6EFSD +-qyi0o9I6CE8H00YpBU+AZYk/fCV3Oe1VaJ6SbKog1zhmZTXBpSq+aO7ybi9aY5MX +-4xajW8fSeMAifk3yYTwsAQKBgErcEcOCOVpItU/uloKPYpRWFjHktK83p46fmP+q +-vOJak1d9KExOBfhuN4caucNBSE1D7l3fzE0CSEjDgg41gRYKMW/Ow8DopybfWlqY +-lBdokNEDVvmgug35dmnC2h9q1DiYdkJJTV57+Lp3U1H/k28lX59Q7h1lb1eDHic7 +-YszzAoGBAOx05dhOiYbzAJSTQu3oBHFn4mTYIqCcDO6cQrEJwPKAq7mAhT0yOk9N +-CrqRV/1aes665829cyTwcAZl6nqbzHv5XjX5+g6vmooCb4oCkq49rumHjoQdrX8D +-RR5b+Spkc1jo4rctCcExzSkgo+K5N3oBVYznecje7O7Z0/qiJE/8 +------END RSA PRIVATE KEY----- ++-----BEGIN PRIVATE KEY----- ++MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDaFU9aXb0VFATq ++r5uzNnmyxB1e4YoFB+6gBlGRJHz+luGK4mFWPiB6x9bLzT0mvqD0yu8HgzZk9dXo ++VpxshAiDPc9q9h4vclVdMnMYeIVkODbfAQCmcAsGl6clpuxo+pthM6UjeQ3eu4OW ++AEAa1FIdfkUWxYvXNC7P+mThMTL34PJKreHNrFwoB9SBZ8b7zowWKKe8YdMdy9QJ ++MmsdLcPKoEKf8+ZpLqZlQzSWDxZm8xTzDcg0etXnekFEw4Oz+FALhZHoyy8xb8+W ++Gju7wvobmk7j4SJwIac6qa+MlPeyiI6LNv/NyLbvwr/FNUQHhHkU5IdFo1H5baHp ++v+X/GyHbAgMBAAECggEAURFe4C68XRuGAF+rN2Fmt+djK6QXlGswb1gp9hRkSpd3 ++3BLvMAoENOAYnsX6l26Bkr3lQRurmrgv/iBEIaqrJ25QrmgzLFwKE4zvcAdNPsYO ++z7MltLktwBOb1MlKVHPkUqvKFXeoikWWUqphKhgHNmN7900UALmrNTDVU0jgs3fB ++o35o8d5SjoC52K4wCTjhPyjt4cdbfbziRs2qFhfGdawidRO1xLlDM4tTTW+5yWGK ++lt0SwyvDVC6XWeNoT3nXyKjXWP7hcYqm0iS7ffL9YzEC2RXNGQUqeR50i9Y0rDdH ++Vqcr+Rqio2ww68zbDWBpC/jU133BSoHuSE1wstxIkQKBgQDxlEr42WJfgdajbZ1a ++hUIeLEgvhezLmD1hcYwZuQCLgizmY2ovvmeAH74koCDEsUUQunPYHsRla7wT3q1/ ++IkR1KgJPwESpkQaKuAqxeEAkv7Gn8Lzcn22jCoRCfGA68wKJz2ECFZDc0RDvRrT/ ++9GhiiGUoO47jv9ezrSDO1eu5/QKBgQDnGfYVMNLiA0fy4AxSyY2vdo7vruOFGpRP ++n94gwxZ+0dQDWHzn3J4rHivxtcyd/MOZv4I8PtYK7tmmjYv1ngQ6sGl4p8bpUtwj ++9++/B1CyB1W5/VPqMkd+Sj0dbejycME55+F6/r4basPXxBFFCfknjAlVvyvbBhUy ++ftNpHxZGtwKBgChJM4t2LPqCW3nbgL8ks9b2SX9rVQbKt4m1dsifWmDpb3VoJMAb ++f4UVRg8ziONkMIFOppzm3JeRNMcXflVSMJpdTA9in9CrN60QbfAUfpXiRc0cz1H3 ++YEAtM8smlKGf/s9efu3rDMJWNv3AC9UXPAUae8wOypBeYKk8+NilQe89AoGAXEA3 ++xFO+CqyGnwQixzVf0qf//NuSRQLMK1DEyc02gJ9gA4niKmgd11Zu8kjBClvo9MnG ++wifPJ4Qa6+pa8UwHoinjoF9Q/rit2cnSMS5JXxegd+MRCU7SzS3zYXkLYSPzbhsL ++Hh7sYmNnFA1XW3jUtZ2n6EusxPyTn5mS6MaZDNcCgYBelFKFjNIQ50NbOnm8DewK ++jUd5OFKowKodlQVcHiF9CVbjvpN8ZPRcBSmqDU4kpT/rmcybVoL6Zfa/zWkw8+Oh ++QxKb3BYf5vRUMd/RA+/t5KG4ZOIIYB3qoltAYlhVaINukL6cGVG1qvV/ntcsfsn6 ++kmf1UgGFcKrJuXgwEtTVxw== ++-----END PRIVATE KEY----- +--- a/examples/standalone/localhost/cert.pem ++++ b/examples/standalone/localhost/cert.pem +@@ -1,13 +1,22 @@ + -----BEGIN CERTIFICATE----- +-MIIB3jCCAYigAwIBAgICBTkwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UEBhMCVVMx +-ETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxKzApBgNVBAoM +-IlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDASBgNVBAMMC2V4 +-YW1wbGUuY29tMB4XDTE0MTIxMTIyMzQ0NVoXDTE0MTIxODIyMzQ0NVowdzELMAkG +-A1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3Ix +-KzApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDAS +-BgNVBAMMC2V4YW1wbGUuY29tMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKx1c7RR +-7R/drnBSQ/zfx1vQLHUbFLh1AQQQ5R8DZUXd36efNK79vukFhN9HFoHZiUvOjm0c +-+pVE6K+EdE/twuUCAwEAATANBgkqhkiG9w0BAQsFAANBAC24z0IdwIVKSlntksll +-vr6zJepBH5fMndfk3XJp10jT6VE+14KNtjh02a56GoraAvJAT5/H67E8GvJ/ocNn +-B/o= ++MIIDjjCCAnagAwIBAgIJALVG/VbBb5U7MA0GCSqGSIb3DQEBCwUAMFsxCzAJBgNV ++BAYTAkFVMQswCQYDVQQIDAJXQTEeMBwGA1UEBwwVVGhlIG1pZGRsZSBvZiBub3do ++ZXJlMR8wHQYDVQQKDBZDZXJ0Ym90IFRlc3QgQ2VydHMgSW5jMCAXDTE2MTEyODIx ++MzUzN1oYDzIyOTAwOTEzMjEzNTM3WjBbMQswCQYDVQQGEwJBVTELMAkGA1UECAwC ++V0ExHjAcBgNVBAcMFVRoZSBtaWRkbGUgb2Ygbm93aGVyZTEfMB0GA1UECgwWQ2Vy ++dGJvdCBUZXN0IENlcnRzIEluYzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ++ggEBANoVT1pdvRUUBOqvm7M2ebLEHV7higUH7qAGUZEkfP6W4YriYVY+IHrH1svN ++PSa+oPTK7weDNmT11ehWnGyECIM9z2r2Hi9yVV0ycxh4hWQ4Nt8BAKZwCwaXpyWm ++7Gj6m2EzpSN5Dd67g5YAQBrUUh1+RRbFi9c0Ls/6ZOExMvfg8kqt4c2sXCgH1IFn ++xvvOjBYop7xh0x3L1Akyax0tw8qgQp/z5mkupmVDNJYPFmbzFPMNyDR61ed6QUTD ++g7P4UAuFkejLLzFvz5YaO7vC+huaTuPhInAhpzqpr4yU97KIjos2/83Itu/Cv8U1 ++RAeEeRTkh0WjUfltoem/5f8bIdsCAwEAAaNTMFEwHQYDVR0OBBYEFHy+bEYqwvFU ++uQLTkIfQ36AM2DQiMB8GA1UdIwQYMBaAFHy+bEYqwvFUuQLTkIfQ36AM2DQiMA8G ++A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAH3ANVzB59FcunZV/F8T ++RiCD6/gV7Jc3CswU8N8tVjzMCg2jOdTFF9iYZzNNKQvG13o/n5LkQr/lkKRQkWTx ++nkE5WZbR7vNqlzXgPa9NBiK5rPjgSt8azPW+Skct3Bj4B3PhTMSpoQ7PsUJ8UeV8 ++kTNR5xrRLt6/mLfRJTXWXBM43GEZi8lL5q0nqz0tPGISADshHMo6ZlUu5Hvfp5v+ ++aonpO4sVS9hGOVxjGNMXYApEUy4jid9jjAfEk6jeELJMbXGLy/botFgIJK/QPe6P ++AfbdFgtg/qzG7Uy0A1iXXfWdgwmVrhCoGYYWCn4yWCAm894QKtdim87CHSDP0WUf ++Esg= + -----END CERTIFICATE----- +--- a/examples/standalone/localhost/key.pem ++++ b/examples/standalone/localhost/key.pem +@@ -1,9 +1,28 @@ +------BEGIN RSA PRIVATE KEY----- +-MIIBOgIBAAJBAKx1c7RR7R/drnBSQ/zfx1vQLHUbFLh1AQQQ5R8DZUXd36efNK79 +-vukFhN9HFoHZiUvOjm0c+pVE6K+EdE/twuUCAwEAAQJAMbrEnJCrQe8YqAbw1/Bn +-elAzIamndfE3U8bTavf9sgFpS4HL83rhd6PDbvx81ucaJAT/5x048fM/nFl4fzAc +-mQIhAOF/a9o3EIsDKEmUl+Z1OaOiUxDF3kqWSmALEsmvDhwXAiEAw8ljV5RO/rUp +-Zu2YMDFq3MKpyyMgBIJ8CxmGRc6gCmMCIGRQzkcmhfqBrhOFwkmozrqIBRIKJIjj +-8TRm2LXWZZ2DAiAqVO7PztdNpynugUy4jtbGKKjBrTSNBRGA7OHlUgm0dQIhALQq +-6oGU29Vxlvt3k0vmiRKU4AVfLyNXIGtcWcNG46h/ +------END RSA PRIVATE KEY----- ++-----BEGIN PRIVATE KEY----- ++MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDaFU9aXb0VFATq ++r5uzNnmyxB1e4YoFB+6gBlGRJHz+luGK4mFWPiB6x9bLzT0mvqD0yu8HgzZk9dXo ++VpxshAiDPc9q9h4vclVdMnMYeIVkODbfAQCmcAsGl6clpuxo+pthM6UjeQ3eu4OW ++AEAa1FIdfkUWxYvXNC7P+mThMTL34PJKreHNrFwoB9SBZ8b7zowWKKe8YdMdy9QJ ++MmsdLcPKoEKf8+ZpLqZlQzSWDxZm8xTzDcg0etXnekFEw4Oz+FALhZHoyy8xb8+W ++Gju7wvobmk7j4SJwIac6qa+MlPeyiI6LNv/NyLbvwr/FNUQHhHkU5IdFo1H5baHp ++v+X/GyHbAgMBAAECggEAURFe4C68XRuGAF+rN2Fmt+djK6QXlGswb1gp9hRkSpd3 ++3BLvMAoENOAYnsX6l26Bkr3lQRurmrgv/iBEIaqrJ25QrmgzLFwKE4zvcAdNPsYO ++z7MltLktwBOb1MlKVHPkUqvKFXeoikWWUqphKhgHNmN7900UALmrNTDVU0jgs3fB ++o35o8d5SjoC52K4wCTjhPyjt4cdbfbziRs2qFhfGdawidRO1xLlDM4tTTW+5yWGK ++lt0SwyvDVC6XWeNoT3nXyKjXWP7hcYqm0iS7ffL9YzEC2RXNGQUqeR50i9Y0rDdH ++Vqcr+Rqio2ww68zbDWBpC/jU133BSoHuSE1wstxIkQKBgQDxlEr42WJfgdajbZ1a ++hUIeLEgvhezLmD1hcYwZuQCLgizmY2ovvmeAH74koCDEsUUQunPYHsRla7wT3q1/ ++IkR1KgJPwESpkQaKuAqxeEAkv7Gn8Lzcn22jCoRCfGA68wKJz2ECFZDc0RDvRrT/ ++9GhiiGUoO47jv9ezrSDO1eu5/QKBgQDnGfYVMNLiA0fy4AxSyY2vdo7vruOFGpRP ++n94gwxZ+0dQDWHzn3J4rHivxtcyd/MOZv4I8PtYK7tmmjYv1ngQ6sGl4p8bpUtwj ++9++/B1CyB1W5/VPqMkd+Sj0dbejycME55+F6/r4basPXxBFFCfknjAlVvyvbBhUy ++ftNpHxZGtwKBgChJM4t2LPqCW3nbgL8ks9b2SX9rVQbKt4m1dsifWmDpb3VoJMAb ++f4UVRg8ziONkMIFOppzm3JeRNMcXflVSMJpdTA9in9CrN60QbfAUfpXiRc0cz1H3 ++YEAtM8smlKGf/s9efu3rDMJWNv3AC9UXPAUae8wOypBeYKk8+NilQe89AoGAXEA3 ++xFO+CqyGnwQixzVf0qf//NuSRQLMK1DEyc02gJ9gA4niKmgd11Zu8kjBClvo9MnG ++wifPJ4Qa6+pa8UwHoinjoF9Q/rit2cnSMS5JXxegd+MRCU7SzS3zYXkLYSPzbhsL ++Hh7sYmNnFA1XW3jUtZ2n6EusxPyTn5mS6MaZDNcCgYBelFKFjNIQ50NbOnm8DewK ++jUd5OFKowKodlQVcHiF9CVbjvpN8ZPRcBSmqDU4kpT/rmcybVoL6Zfa/zWkw8+Oh ++QxKb3BYf5vRUMd/RA+/t5KG4ZOIIYB3qoltAYlhVaINukL6cGVG1qvV/ntcsfsn6 ++kmf1UgGFcKrJuXgwEtTVxw== ++-----END PRIVATE KEY----- diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..6de96c3 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1 @@ +fix-tests-under-openssl-1-1.patch diff --git a/debian/rules b/debian/rules index ec939d9..dd1116c 100755 --- a/debian/rules +++ b/debian/rules @@ -7,7 +7,10 @@ export PYBUILD_NAME = acme override_dh_auto_build: dh_auto_build - PYTHONPATH=. http_proxy='127.0.0.1:9' sphinx-build -N -bhtml docs/ build/html + PYTHONPATH=. \ + http_proxy='127.0.0.1:9' \ + https_proxy='127.0.0.1:9' \ + sphinx-build -N -bhtml docs/ build/html override_dh_auto_install: dh_auto_install diff --git a/examples/standalone/localhost/cert.pem b/examples/standalone/localhost/cert.pem index 569366a..96c55cb 120000..100644 --- a/examples/standalone/localhost/cert.pem +++ b/examples/standalone/localhost/cert.pem @@ -1 +1,13 @@ -../../../acme/testdata/cert.pem
\ No newline at end of file +-----BEGIN CERTIFICATE----- +MIIB3jCCAYigAwIBAgICBTkwDQYJKoZIhvcNAQELBQAwdzELMAkGA1UEBhMCVVMx +ETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3IxKzApBgNVBAoM +IlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDASBgNVBAMMC2V4 +YW1wbGUuY29tMB4XDTE0MTIxMTIyMzQ0NVoXDTE0MTIxODIyMzQ0NVowdzELMAkG +A1UEBhMCVVMxETAPBgNVBAgMCE1pY2hpZ2FuMRIwEAYDVQQHDAlBbm4gQXJib3Ix +KzApBgNVBAoMIlVuaXZlcnNpdHkgb2YgTWljaGlnYW4gYW5kIHRoZSBFRkYxFDAS +BgNVBAMMC2V4YW1wbGUuY29tMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKx1c7RR +7R/drnBSQ/zfx1vQLHUbFLh1AQQQ5R8DZUXd36efNK79vukFhN9HFoHZiUvOjm0c ++pVE6K+EdE/twuUCAwEAATANBgkqhkiG9w0BAQsFAANBAC24z0IdwIVKSlntksll +vr6zJepBH5fMndfk3XJp10jT6VE+14KNtjh02a56GoraAvJAT5/H67E8GvJ/ocNn +B/o= +-----END CERTIFICATE----- diff --git a/examples/standalone/localhost/key.pem b/examples/standalone/localhost/key.pem index 870f4f8..610c8d3 120000..100644 --- a/examples/standalone/localhost/key.pem +++ b/examples/standalone/localhost/key.pem @@ -1 +1,9 @@ -../../../acme/testdata/rsa512_key.pem
\ No newline at end of file +-----BEGIN RSA PRIVATE KEY----- +MIIBOgIBAAJBAKx1c7RR7R/drnBSQ/zfx1vQLHUbFLh1AQQQ5R8DZUXd36efNK79 +vukFhN9HFoHZiUvOjm0c+pVE6K+EdE/twuUCAwEAAQJAMbrEnJCrQe8YqAbw1/Bn +elAzIamndfE3U8bTavf9sgFpS4HL83rhd6PDbvx81ucaJAT/5x048fM/nFl4fzAc +mQIhAOF/a9o3EIsDKEmUl+Z1OaOiUxDF3kqWSmALEsmvDhwXAiEAw8ljV5RO/rUp +Zu2YMDFq3MKpyyMgBIJ8CxmGRc6gCmMCIGRQzkcmhfqBrhOFwkmozrqIBRIKJIjj +8TRm2LXWZZ2DAiAqVO7PztdNpynugUy4jtbGKKjBrTSNBRGA7OHlUgm0dQIhALQq +6oGU29Vxlvt3k0vmiRKU4AVfLyNXIGtcWcNG46h/ +-----END RSA PRIVATE KEY----- @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.8.1' +version = '0.9.3' # Please update tox.ini when modifying dependency version requirements install_requires = [ @@ -35,6 +35,11 @@ if sys.version_info < (2, 7): else: install_requires.append('mock') +# dnspython 1.12 is required to support both Python 2 and Python 3. +dns_extras = [ + 'dnspython>=1.12', +] + dev_extras = [ 'nose', 'pep8', @@ -76,6 +81,7 @@ setup( include_package_data=True, install_requires=install_requires, extras_require={ + 'dns': dns_extras, 'dev': dev_extras, 'docs': docs_extras, }, |
