From 30803f30ba63742cf98f4c6fbfcf890317d558ae Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Mon, 4 Feb 2019 13:03:29 -0500 Subject: [PATCH] acme: add TLSALPN01Response for initiating tls-alpn-01. (#6689) The existing `acme.TLSALPN01` challenge class did not have a `response_cls`, meaning it was not possible to use a tls-alpn-01 challenge with `client.answer_challenge`. To support the above a simple `TLSALPN01Response` class is added that doesn't provide the ability to solve a tls-alpn-01 challenge end to end (e.g. generating and serving the correct response certificate) but that does allow the challenge to be initiated. This is sufficient for users that have set up the challenge response independent of the `acme` module code. Resolves #6676 --- CHANGELOG.md | 2 ++ acme/acme/challenges.py | 12 ++++++++++++ acme/acme/challenges_test.py | 27 +++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d45af20e8..cae93c5b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ Certbot adheres to [Semantic Versioning](https://semver.org/). * Avoid to process again challenges that are already validated when a certificate is issued. +* Support for initiating (but not solving end-to-end) TLS-ALPN-01 challenges + with the `acme` module. ### Changed diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index a65768228..501f74881 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -513,6 +513,17 @@ class TLSSNI01(KeyAuthorizationChallenge): return self.response(account_key).gen_cert(key=kwargs.get('cert_key')) +@ChallengeResponse.register +class TLSALPN01Response(KeyAuthorizationChallengeResponse): + """ACME TLS-ALPN-01 challenge response. + + This class only allows initiating a TLS-ALPN-01 challenge returned from the + CA. Full support for responding to TLS-ALPN-01 challenges by generating and + serving the expected response certificate is not currently provided. + """ + typ = "tls-alpn-01" + + @Challenge.register # pylint: disable=too-many-ancestors class TLSALPN01(KeyAuthorizationChallenge): """ACME tls-alpn-01 challenge. @@ -522,6 +533,7 @@ class TLSALPN01(KeyAuthorizationChallenge): """ typ = "tls-alpn-01" + response_cls = TLSALPN01Response def validation(self, account_key, **kwargs): """Generate validation for the challenge.""" diff --git a/acme/acme/challenges_test.py b/acme/acme/challenges_test.py index 9307eb95b..81d39058e 100644 --- a/acme/acme/challenges_test.py +++ b/acme/acme/challenges_test.py @@ -402,6 +402,33 @@ class TLSSNI01Test(unittest.TestCase): KEY, cert_key=mock.sentinel.cert_key)) mock_gen_cert.assert_called_once_with(key=mock.sentinel.cert_key) +class TLSALPN01ResponseTest(unittest.TestCase): + # pylint: disable=too-many-instance-attributes + + def setUp(self): + from acme.challenges import TLSALPN01Response + self.msg = TLSALPN01Response(key_authorization=u'foo') + self.jmsg = { + 'resource': 'challenge', + 'type': 'tls-alpn-01', + 'keyAuthorization': u'foo', + } + + from acme.challenges import TLSALPN01 + self.chall = TLSALPN01(token=(b'x' * 16)) + self.response = self.chall.response(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 TLSALPN01Response + self.assertEqual(self.msg, TLSALPN01Response.from_json(self.jmsg)) + + def test_from_json_hashable(self): + from acme.challenges import TLSALPN01Response + hash(TLSALPN01Response.from_json(self.jmsg)) + class TLSALPN01Test(unittest.TestCase):