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):