summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobie Basak <robie@justgohome.co.uk>2017-02-24 21:19:33 (GMT)
committerRobie Basak <robie@justgohome.co.uk>2017-02-24 21:19:33 (GMT)
commit2de942f7a60d7a04900eedacbf68d8e736fd9b1a (patch)
treeb1d92ccc8e83bc24c7f99e973f0e3da26c4401dc
parentc23f259317dfbae96b2ad9cc3d9ee06a489a1f4c (diff)
-rw-r--r--PKG-INFO2
-rw-r--r--acme.egg-info/PKG-INFO2
-rw-r--r--acme.egg-info/SOURCES.txt2
-rw-r--r--acme.egg-info/requires.txt3
-rw-r--r--acme/challenges.py76
-rw-r--r--acme/challenges_test.py103
-rw-r--r--acme/client.py9
-rw-r--r--acme/client_test.py6
-rw-r--r--acme/dns_resolver.py45
-rw-r--r--acme/dns_resolver_test.py77
-rw-r--r--acme/errors.py6
-rw-r--r--acme/fields.py2
-rw-r--r--acme/messages.py2
-rw-r--r--acme/test_util.py38
-rw-r--r--acme/util.py18
-rw-r--r--acme/util_test.py18
-rw-r--r--debian/changelog24
-rw-r--r--debian/control4
-rw-r--r--debian/patches/fix-tests-under-openssl-1-1.patch245
-rw-r--r--debian/patches/series1
-rwxr-xr-xdebian/rules5
-rw-r--r--[l---------]examples/standalone/localhost/cert.pem14
-rw-r--r--[l---------]examples/standalone/localhost/key.pem10
-rw-r--r--setup.py8
24 files changed, 693 insertions, 27 deletions
diff --git a/PKG-INFO b/PKG-INFO
index 06ac790..d917980 100644
--- a/PKG-INFO
+++ b/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/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-----
diff --git a/setup.py b/setup.py
index 2bcec91..e0c1a90 100644
--- a/setup.py
+++ b/setup.py
@@ -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,
},