2015-05-10 07:26:21 -04:00
|
|
|
"""Tests for acme.challenges."""
|
2021-02-09 14:43:15 -05:00
|
|
|
import urllib.parse as urllib_parse
|
2015-02-12 03:45:03 -05:00
|
|
|
import unittest
|
2021-02-05 18:51:18 -05:00
|
|
|
from unittest import mock
|
2015-02-12 03:45:03 -05:00
|
|
|
|
2017-12-11 14:25:09 -05:00
|
|
|
import josepy as jose
|
Reimplement tls-alpn-01 in acme (#6886)
This PR is the first part of work described in #6724.
It reintroduces the tls-alpn-01 challenge in `acme` module, that was introduced by #5894 and reverted by #6100. The reason it was removed in the past is because some tests showed that with `1.0.2` branch of OpenSSL, the self-signed certificate containing the authorization key is sent to the requester even if the ALPN protocol `acme-tls/1` was not declared as supported by the requester during the TLS handshake.
However recent discussions lead to the conclusion that this behavior was not a security issue, because first it is coherent with the behavior with servers that do not support ALPN at all, and second it cannot make a tls-alpn-01 challenge be validated in this kind of corner case.
On top of the original modifications given by #5894, I merged the code to be up-to-date with our `master`, and fixed tests to match recent evolution about not displaying the `keyAuthorization` in the deserialized JSON form of an ACME challenge.
I also move the logic to verify if ALPN is available on the current system, and so that the tls-alpn-01 challenge can be used, to a dedicated static function `is_available` in `acme.challenge.TLSALPN01`. This function is used in the related tests to skip them, and will be used in the future from Certbot plugins to trigger or not the logic related to tls-alpn-01, depending on the OpenSSL version available to Python.
* Reimplement TLS-ALPN-01 challenge and standalone TLS-ALPN server from #5894.
* Setup a class method to check if tls-alpn-01 is supported.
* Add potential missing parameter in validation for tls-alpn
* Improve comments
* Make a class private
* Handle old versions of openssl that do not terminate the handshake when they should do.
* Add changelog
* Explicitly close the TLS connection by the book.
* Remove unused exception
* Fix lint
2020-03-12 16:53:19 -04:00
|
|
|
import OpenSSL
|
2015-06-24 16:32:42 -04:00
|
|
|
import requests
|
2022-01-21 04:29:53 -05:00
|
|
|
from josepy.jwk import JWKEC
|
2015-07-12 07:26:05 -04:00
|
|
|
|
Reimplement tls-alpn-01 in acme (#6886)
This PR is the first part of work described in #6724.
It reintroduces the tls-alpn-01 challenge in `acme` module, that was introduced by #5894 and reverted by #6100. The reason it was removed in the past is because some tests showed that with `1.0.2` branch of OpenSSL, the self-signed certificate containing the authorization key is sent to the requester even if the ALPN protocol `acme-tls/1` was not declared as supported by the requester during the TLS handshake.
However recent discussions lead to the conclusion that this behavior was not a security issue, because first it is coherent with the behavior with servers that do not support ALPN at all, and second it cannot make a tls-alpn-01 challenge be validated in this kind of corner case.
On top of the original modifications given by #5894, I merged the code to be up-to-date with our `master`, and fixed tests to match recent evolution about not displaying the `keyAuthorization` in the deserialized JSON form of an ACME challenge.
I also move the logic to verify if ALPN is available on the current system, and so that the tls-alpn-01 challenge can be used, to a dedicated static function `is_available` in `acme.challenge.TLSALPN01`. This function is used in the related tests to skip them, and will be used in the future from Certbot plugins to trigger or not the logic related to tls-alpn-01, depending on the OpenSSL version available to Python.
* Reimplement TLS-ALPN-01 challenge and standalone TLS-ALPN server from #5894.
* Setup a class method to check if tls-alpn-01 is supported.
* Add potential missing parameter in validation for tls-alpn
* Improve comments
* Make a class private
* Handle old versions of openssl that do not terminate the handshake when they should do.
* Add changelog
* Explicitly close the TLS connection by the book.
* Remove unused exception
* Fix lint
2020-03-12 16:53:19 -04:00
|
|
|
from acme import errors
|
|
|
|
|
|
2019-11-26 18:25:41 -05:00
|
|
|
import test_util
|
2015-02-12 03:45:03 -05:00
|
|
|
|
2015-12-23 13:48:52 -05:00
|
|
|
CERT = test_util.load_comparable_cert('cert.pem')
|
2015-10-28 15:42:06 -04:00
|
|
|
KEY = jose.JWKRSA(key=test_util.load_rsa_private_key('rsa512_key.pem'))
|
2015-02-12 03:45:03 -05:00
|
|
|
|
|
|
|
|
|
2015-09-29 03:02:33 -04:00
|
|
|
class ChallengeTest(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def test_from_json_unrecognized(self):
|
|
|
|
|
from acme.challenges import Challenge
|
|
|
|
|
from acme.challenges import UnrecognizedChallenge
|
|
|
|
|
chall = UnrecognizedChallenge({"type": "foo"})
|
|
|
|
|
self.assertEqual(chall, Challenge.from_json(chall.jobj))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UnrecognizedChallengeTest(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
from acme.challenges import UnrecognizedChallenge
|
|
|
|
|
self.jobj = {"type": "foo"}
|
|
|
|
|
self.chall = UnrecognizedChallenge(self.jobj)
|
|
|
|
|
|
|
|
|
|
def test_to_partial_json(self):
|
|
|
|
|
self.assertEqual(self.jobj, self.chall.to_partial_json())
|
|
|
|
|
|
|
|
|
|
def test_from_json(self):
|
|
|
|
|
from acme.challenges import UnrecognizedChallenge
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
self.chall, UnrecognizedChallenge.from_json(self.jobj))
|
|
|
|
|
|
|
|
|
|
|
2015-10-28 16:41:15 -04:00
|
|
|
class KeyAuthorizationChallengeResponseTest(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
def _encode(name):
|
|
|
|
|
assert name == "token"
|
|
|
|
|
return "foo"
|
|
|
|
|
self.chall = mock.Mock()
|
|
|
|
|
self.chall.encode.side_effect = _encode
|
|
|
|
|
|
|
|
|
|
def test_verify_ok(self):
|
|
|
|
|
from acme.challenges import KeyAuthorizationChallengeResponse
|
|
|
|
|
response = KeyAuthorizationChallengeResponse(
|
|
|
|
|
key_authorization='foo.oKGqedy-b-acd5eoybm2f-NVFxvyOoET5CNy3xnv8WY')
|
|
|
|
|
self.assertTrue(response.verify(self.chall, KEY.public_key()))
|
|
|
|
|
|
|
|
|
|
def test_verify_wrong_token(self):
|
|
|
|
|
from acme.challenges import KeyAuthorizationChallengeResponse
|
|
|
|
|
response = KeyAuthorizationChallengeResponse(
|
|
|
|
|
key_authorization='bar.oKGqedy-b-acd5eoybm2f-NVFxvyOoET5CNy3xnv8WY')
|
|
|
|
|
self.assertFalse(response.verify(self.chall, KEY.public_key()))
|
|
|
|
|
|
|
|
|
|
def test_verify_wrong_thumbprint(self):
|
|
|
|
|
from acme.challenges import KeyAuthorizationChallengeResponse
|
|
|
|
|
response = KeyAuthorizationChallengeResponse(
|
|
|
|
|
key_authorization='foo.oKGqedy-b-acd5eoybm2f-NVFxv')
|
|
|
|
|
self.assertFalse(response.verify(self.chall, KEY.public_key()))
|
|
|
|
|
|
|
|
|
|
def test_verify_wrong_form(self):
|
|
|
|
|
from acme.challenges import KeyAuthorizationChallengeResponse
|
|
|
|
|
response = KeyAuthorizationChallengeResponse(
|
2016-01-10 13:17:35 -05:00
|
|
|
key_authorization='.foo.oKGqedy-b-acd5eoybm2f-'
|
|
|
|
|
'NVFxvyOoET5CNy3xnv8WY')
|
2015-10-28 16:41:15 -04:00
|
|
|
self.assertFalse(response.verify(self.chall, KEY.public_key()))
|
|
|
|
|
|
2016-01-05 20:33:30 -05:00
|
|
|
|
2016-01-01 20:55:52 -05:00
|
|
|
class DNS01ResponseTest(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
def test_to_partial_json(self):
|
2019-02-27 12:21:47 -05:00
|
|
|
self.assertEqual({k: v for k, v in self.jmsg.items() if k != 'keyAuthorization'},
|
|
|
|
|
self.msg.to_partial_json())
|
2016-01-01 20:55:52 -05:00
|
|
|
|
|
|
|
|
def test_from_json(self):
|
|
|
|
|
from acme.challenges import DNS01Response
|
2016-01-05 20:33:30 -05:00
|
|
|
self.assertEqual(self.msg, DNS01Response.from_json(self.jmsg))
|
2016-01-01 20:55:52 -05:00
|
|
|
|
|
|
|
|
def test_from_json_hashable(self):
|
|
|
|
|
from acme.challenges import DNS01Response
|
|
|
|
|
hash(DNS01Response.from_json(self.jmsg))
|
|
|
|
|
|
2017-01-30 19:55:54 -05:00
|
|
|
def test_simple_verify_failure(self):
|
2016-01-01 20:55:52 -05:00
|
|
|
key2 = jose.JWKRSA.load(test_util.load_vector('rsa256_key.pem'))
|
2017-01-30 19:55:54 -05:00
|
|
|
public_key = key2.public_key()
|
|
|
|
|
verified = self.response.simple_verify(self.chall, "local", public_key)
|
|
|
|
|
self.assertFalse(verified)
|
|
|
|
|
|
|
|
|
|
def test_simple_verify_success(self):
|
|
|
|
|
public_key = KEY.public_key()
|
|
|
|
|
verified = self.response.simple_verify(self.chall, "local", public_key)
|
|
|
|
|
self.assertTrue(verified)
|
2016-01-01 20:55:52 -05:00
|
|
|
|
2016-01-05 22:25:24 -05:00
|
|
|
|
2016-01-01 20:55:52 -05:00
|
|
|
class DNS01Test(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
from acme.challenges import DNS01
|
2016-01-06 02:44:19 -05:00
|
|
|
self.msg = DNS01(token=jose.decode_b64jose(
|
|
|
|
|
'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA'))
|
2016-01-01 20:55:52 -05:00
|
|
|
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):
|
2016-01-02 01:42:47 -05:00
|
|
|
self.assertEqual(
|
2016-01-04 19:46:28 -05:00
|
|
|
"rAa7iIg4K2y63fvUhCfy8dP1Xl7wEhmQq0oChTcE3Zk",
|
|
|
|
|
self.msg.validation(KEY))
|
2016-01-01 20:55:52 -05:00
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
|
2015-10-28 16:41:15 -04:00
|
|
|
|
|
|
|
|
class HTTP01ResponseTest(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
from acme.challenges import HTTP01Response
|
|
|
|
|
self.msg = HTTP01Response(key_authorization=u'foo')
|
|
|
|
|
self.jmsg = {
|
|
|
|
|
'resource': 'challenge',
|
|
|
|
|
'type': 'http-01',
|
|
|
|
|
'keyAuthorization': u'foo',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
from acme.challenges import HTTP01
|
|
|
|
|
self.chall = HTTP01(token=(b'x' * 16))
|
|
|
|
|
self.response = self.chall.response(KEY)
|
|
|
|
|
|
|
|
|
|
def test_to_partial_json(self):
|
2019-02-27 12:21:47 -05:00
|
|
|
self.assertEqual({k: v for k, v in self.jmsg.items() if k != 'keyAuthorization'},
|
|
|
|
|
self.msg.to_partial_json())
|
2015-10-28 16:41:15 -04:00
|
|
|
|
|
|
|
|
def test_from_json(self):
|
|
|
|
|
from acme.challenges import HTTP01Response
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
self.msg, HTTP01Response.from_json(self.jmsg))
|
|
|
|
|
|
|
|
|
|
def test_from_json_hashable(self):
|
|
|
|
|
from acme.challenges import HTTP01Response
|
|
|
|
|
hash(HTTP01Response.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.challenges.requests.get")
|
|
|
|
|
def test_simple_verify_good_validation(self, mock_get):
|
|
|
|
|
validation = self.chall.validation(KEY)
|
2015-11-24 03:42:59 -05:00
|
|
|
mock_get.return_value = mock.MagicMock(text=validation)
|
2015-10-28 16:41:15 -04:00
|
|
|
self.assertTrue(self.response.simple_verify(
|
|
|
|
|
self.chall, "local", KEY.public_key()))
|
2019-02-05 13:37:09 -05:00
|
|
|
mock_get.assert_called_once_with(self.chall.uri("local"), verify=False)
|
2015-10-28 16:41:15 -04:00
|
|
|
|
|
|
|
|
@mock.patch("acme.challenges.requests.get")
|
|
|
|
|
def test_simple_verify_bad_validation(self, mock_get):
|
2015-11-24 03:42:59 -05:00
|
|
|
mock_get.return_value = mock.MagicMock(text="!")
|
2015-10-28 16:41:15 -04:00
|
|
|
self.assertFalse(self.response.simple_verify(
|
|
|
|
|
self.chall, "local", KEY.public_key()))
|
|
|
|
|
|
2015-11-20 19:39:03 -05:00
|
|
|
@mock.patch("acme.challenges.requests.get")
|
|
|
|
|
def test_simple_verify_whitespace_validation(self, mock_get):
|
|
|
|
|
from acme.challenges import HTTP01Response
|
|
|
|
|
mock_get.return_value = mock.MagicMock(
|
|
|
|
|
text=(self.chall.validation(KEY) +
|
2015-11-24 03:42:59 -05:00
|
|
|
HTTP01Response.WHITESPACE_CUTSET))
|
2015-11-20 19:39:03 -05:00
|
|
|
self.assertTrue(self.response.simple_verify(
|
|
|
|
|
self.chall, "local", KEY.public_key()))
|
2019-02-05 13:37:09 -05:00
|
|
|
mock_get.assert_called_once_with(self.chall.uri("local"), verify=False)
|
2015-11-20 19:39:03 -05:00
|
|
|
|
2015-10-28 16:41:15 -04:00
|
|
|
@mock.patch("acme.challenges.requests.get")
|
|
|
|
|
def test_simple_verify_connection_error(self, mock_get):
|
|
|
|
|
mock_get.side_effect = requests.exceptions.RequestException
|
|
|
|
|
self.assertFalse(self.response.simple_verify(
|
|
|
|
|
self.chall, "local", KEY.public_key()))
|
|
|
|
|
|
|
|
|
|
@mock.patch("acme.challenges.requests.get")
|
|
|
|
|
def test_simple_verify_port(self, mock_get):
|
|
|
|
|
self.response.simple_verify(
|
|
|
|
|
self.chall, domain="local",
|
|
|
|
|
account_public_key=KEY.public_key(), port=8080)
|
|
|
|
|
self.assertEqual("local:8080", urllib_parse.urlparse(
|
|
|
|
|
mock_get.mock_calls[0][1][0]).netloc)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HTTP01Test(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
from acme.challenges import HTTP01
|
|
|
|
|
self.msg = HTTP01(
|
|
|
|
|
token=jose.decode_b64jose(
|
|
|
|
|
'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA'))
|
|
|
|
|
self.jmsg = {
|
|
|
|
|
'type': 'http-01',
|
|
|
|
|
'token': 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def test_path(self):
|
|
|
|
|
self.assertEqual(self.msg.path, '/.well-known/acme-challenge/'
|
|
|
|
|
'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA')
|
|
|
|
|
|
|
|
|
|
def test_uri(self):
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
'http://example.com/.well-known/acme-challenge/'
|
|
|
|
|
'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA',
|
|
|
|
|
self.msg.uri('example.com'))
|
|
|
|
|
|
|
|
|
|
def test_to_partial_json(self):
|
|
|
|
|
self.assertEqual(self.jmsg, self.msg.to_partial_json())
|
|
|
|
|
|
|
|
|
|
def test_from_json(self):
|
|
|
|
|
from acme.challenges import HTTP01
|
|
|
|
|
self.assertEqual(self.msg, HTTP01.from_json(self.jmsg))
|
|
|
|
|
|
|
|
|
|
def test_from_json_hashable(self):
|
|
|
|
|
from acme.challenges import HTTP01
|
|
|
|
|
hash(HTTP01.from_json(self.jmsg))
|
|
|
|
|
|
|
|
|
|
def test_good_token(self):
|
|
|
|
|
self.assertTrue(self.msg.good_token)
|
|
|
|
|
self.assertFalse(
|
|
|
|
|
self.msg.update(token=b'..').good_token)
|
|
|
|
|
|
|
|
|
|
|
2019-02-04 13:03:29 -05:00
|
|
|
class TLSALPN01ResponseTest(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
Reimplement tls-alpn-01 in acme (#6886)
This PR is the first part of work described in #6724.
It reintroduces the tls-alpn-01 challenge in `acme` module, that was introduced by #5894 and reverted by #6100. The reason it was removed in the past is because some tests showed that with `1.0.2` branch of OpenSSL, the self-signed certificate containing the authorization key is sent to the requester even if the ALPN protocol `acme-tls/1` was not declared as supported by the requester during the TLS handshake.
However recent discussions lead to the conclusion that this behavior was not a security issue, because first it is coherent with the behavior with servers that do not support ALPN at all, and second it cannot make a tls-alpn-01 challenge be validated in this kind of corner case.
On top of the original modifications given by #5894, I merged the code to be up-to-date with our `master`, and fixed tests to match recent evolution about not displaying the `keyAuthorization` in the deserialized JSON form of an ACME challenge.
I also move the logic to verify if ALPN is available on the current system, and so that the tls-alpn-01 challenge can be used, to a dedicated static function `is_available` in `acme.challenge.TLSALPN01`. This function is used in the related tests to skip them, and will be used in the future from Certbot plugins to trigger or not the logic related to tls-alpn-01, depending on the OpenSSL version available to Python.
* Reimplement TLS-ALPN-01 challenge and standalone TLS-ALPN server from #5894.
* Setup a class method to check if tls-alpn-01 is supported.
* Add potential missing parameter in validation for tls-alpn
* Improve comments
* Make a class private
* Handle old versions of openssl that do not terminate the handshake when they should do.
* Add changelog
* Explicitly close the TLS connection by the book.
* Remove unused exception
* Fix lint
2020-03-12 16:53:19 -04:00
|
|
|
from acme.challenges import TLSALPN01
|
|
|
|
|
self.chall = TLSALPN01(
|
|
|
|
|
token=jose.b64decode(b'a82d5ff8ef740d12881f6d3c2277ab2e'))
|
|
|
|
|
self.domain = u'example.com'
|
|
|
|
|
self.domain2 = u'example2.com'
|
|
|
|
|
|
|
|
|
|
self.response = self.chall.response(KEY)
|
2019-02-04 13:03:29 -05:00
|
|
|
self.jmsg = {
|
|
|
|
|
'resource': 'challenge',
|
|
|
|
|
'type': 'tls-alpn-01',
|
Reimplement tls-alpn-01 in acme (#6886)
This PR is the first part of work described in #6724.
It reintroduces the tls-alpn-01 challenge in `acme` module, that was introduced by #5894 and reverted by #6100. The reason it was removed in the past is because some tests showed that with `1.0.2` branch of OpenSSL, the self-signed certificate containing the authorization key is sent to the requester even if the ALPN protocol `acme-tls/1` was not declared as supported by the requester during the TLS handshake.
However recent discussions lead to the conclusion that this behavior was not a security issue, because first it is coherent with the behavior with servers that do not support ALPN at all, and second it cannot make a tls-alpn-01 challenge be validated in this kind of corner case.
On top of the original modifications given by #5894, I merged the code to be up-to-date with our `master`, and fixed tests to match recent evolution about not displaying the `keyAuthorization` in the deserialized JSON form of an ACME challenge.
I also move the logic to verify if ALPN is available on the current system, and so that the tls-alpn-01 challenge can be used, to a dedicated static function `is_available` in `acme.challenge.TLSALPN01`. This function is used in the related tests to skip them, and will be used in the future from Certbot plugins to trigger or not the logic related to tls-alpn-01, depending on the OpenSSL version available to Python.
* Reimplement TLS-ALPN-01 challenge and standalone TLS-ALPN server from #5894.
* Setup a class method to check if tls-alpn-01 is supported.
* Add potential missing parameter in validation for tls-alpn
* Improve comments
* Make a class private
* Handle old versions of openssl that do not terminate the handshake when they should do.
* Add changelog
* Explicitly close the TLS connection by the book.
* Remove unused exception
* Fix lint
2020-03-12 16:53:19 -04:00
|
|
|
'keyAuthorization': self.response.key_authorization,
|
2019-02-04 13:03:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def test_to_partial_json(self):
|
2019-02-27 12:21:47 -05:00
|
|
|
self.assertEqual({k: v for k, v in self.jmsg.items() if k != 'keyAuthorization'},
|
Reimplement tls-alpn-01 in acme (#6886)
This PR is the first part of work described in #6724.
It reintroduces the tls-alpn-01 challenge in `acme` module, that was introduced by #5894 and reverted by #6100. The reason it was removed in the past is because some tests showed that with `1.0.2` branch of OpenSSL, the self-signed certificate containing the authorization key is sent to the requester even if the ALPN protocol `acme-tls/1` was not declared as supported by the requester during the TLS handshake.
However recent discussions lead to the conclusion that this behavior was not a security issue, because first it is coherent with the behavior with servers that do not support ALPN at all, and second it cannot make a tls-alpn-01 challenge be validated in this kind of corner case.
On top of the original modifications given by #5894, I merged the code to be up-to-date with our `master`, and fixed tests to match recent evolution about not displaying the `keyAuthorization` in the deserialized JSON form of an ACME challenge.
I also move the logic to verify if ALPN is available on the current system, and so that the tls-alpn-01 challenge can be used, to a dedicated static function `is_available` in `acme.challenge.TLSALPN01`. This function is used in the related tests to skip them, and will be used in the future from Certbot plugins to trigger or not the logic related to tls-alpn-01, depending on the OpenSSL version available to Python.
* Reimplement TLS-ALPN-01 challenge and standalone TLS-ALPN server from #5894.
* Setup a class method to check if tls-alpn-01 is supported.
* Add potential missing parameter in validation for tls-alpn
* Improve comments
* Make a class private
* Handle old versions of openssl that do not terminate the handshake when they should do.
* Add changelog
* Explicitly close the TLS connection by the book.
* Remove unused exception
* Fix lint
2020-03-12 16:53:19 -04:00
|
|
|
self.response.to_partial_json())
|
2019-02-04 13:03:29 -05:00
|
|
|
|
|
|
|
|
def test_from_json(self):
|
|
|
|
|
from acme.challenges import TLSALPN01Response
|
Reimplement tls-alpn-01 in acme (#6886)
This PR is the first part of work described in #6724.
It reintroduces the tls-alpn-01 challenge in `acme` module, that was introduced by #5894 and reverted by #6100. The reason it was removed in the past is because some tests showed that with `1.0.2` branch of OpenSSL, the self-signed certificate containing the authorization key is sent to the requester even if the ALPN protocol `acme-tls/1` was not declared as supported by the requester during the TLS handshake.
However recent discussions lead to the conclusion that this behavior was not a security issue, because first it is coherent with the behavior with servers that do not support ALPN at all, and second it cannot make a tls-alpn-01 challenge be validated in this kind of corner case.
On top of the original modifications given by #5894, I merged the code to be up-to-date with our `master`, and fixed tests to match recent evolution about not displaying the `keyAuthorization` in the deserialized JSON form of an ACME challenge.
I also move the logic to verify if ALPN is available on the current system, and so that the tls-alpn-01 challenge can be used, to a dedicated static function `is_available` in `acme.challenge.TLSALPN01`. This function is used in the related tests to skip them, and will be used in the future from Certbot plugins to trigger or not the logic related to tls-alpn-01, depending on the OpenSSL version available to Python.
* Reimplement TLS-ALPN-01 challenge and standalone TLS-ALPN server from #5894.
* Setup a class method to check if tls-alpn-01 is supported.
* Add potential missing parameter in validation for tls-alpn
* Improve comments
* Make a class private
* Handle old versions of openssl that do not terminate the handshake when they should do.
* Add changelog
* Explicitly close the TLS connection by the book.
* Remove unused exception
* Fix lint
2020-03-12 16:53:19 -04:00
|
|
|
self.assertEqual(self.response, TLSALPN01Response.from_json(self.jmsg))
|
2019-02-04 13:03:29 -05:00
|
|
|
|
|
|
|
|
def test_from_json_hashable(self):
|
|
|
|
|
from acme.challenges import TLSALPN01Response
|
|
|
|
|
hash(TLSALPN01Response.from_json(self.jmsg))
|
|
|
|
|
|
Reimplement tls-alpn-01 in acme (#6886)
This PR is the first part of work described in #6724.
It reintroduces the tls-alpn-01 challenge in `acme` module, that was introduced by #5894 and reverted by #6100. The reason it was removed in the past is because some tests showed that with `1.0.2` branch of OpenSSL, the self-signed certificate containing the authorization key is sent to the requester even if the ALPN protocol `acme-tls/1` was not declared as supported by the requester during the TLS handshake.
However recent discussions lead to the conclusion that this behavior was not a security issue, because first it is coherent with the behavior with servers that do not support ALPN at all, and second it cannot make a tls-alpn-01 challenge be validated in this kind of corner case.
On top of the original modifications given by #5894, I merged the code to be up-to-date with our `master`, and fixed tests to match recent evolution about not displaying the `keyAuthorization` in the deserialized JSON form of an ACME challenge.
I also move the logic to verify if ALPN is available on the current system, and so that the tls-alpn-01 challenge can be used, to a dedicated static function `is_available` in `acme.challenge.TLSALPN01`. This function is used in the related tests to skip them, and will be used in the future from Certbot plugins to trigger or not the logic related to tls-alpn-01, depending on the OpenSSL version available to Python.
* Reimplement TLS-ALPN-01 challenge and standalone TLS-ALPN server from #5894.
* Setup a class method to check if tls-alpn-01 is supported.
* Add potential missing parameter in validation for tls-alpn
* Improve comments
* Make a class private
* Handle old versions of openssl that do not terminate the handshake when they should do.
* Add changelog
* Explicitly close the TLS connection by the book.
* Remove unused exception
* Fix lint
2020-03-12 16:53:19 -04:00
|
|
|
def test_gen_verify_cert(self):
|
|
|
|
|
key1 = test_util.load_pyopenssl_private_key('rsa512_key.pem')
|
|
|
|
|
cert, key2 = self.response.gen_cert(self.domain, key1)
|
|
|
|
|
self.assertEqual(key1, key2)
|
|
|
|
|
self.assertTrue(self.response.verify_cert(self.domain, cert))
|
|
|
|
|
|
|
|
|
|
def test_gen_verify_cert_gen_key(self):
|
|
|
|
|
cert, key = self.response.gen_cert(self.domain)
|
2021-04-28 20:45:08 -04:00
|
|
|
self.assertIsInstance(key, OpenSSL.crypto.PKey)
|
Reimplement tls-alpn-01 in acme (#6886)
This PR is the first part of work described in #6724.
It reintroduces the tls-alpn-01 challenge in `acme` module, that was introduced by #5894 and reverted by #6100. The reason it was removed in the past is because some tests showed that with `1.0.2` branch of OpenSSL, the self-signed certificate containing the authorization key is sent to the requester even if the ALPN protocol `acme-tls/1` was not declared as supported by the requester during the TLS handshake.
However recent discussions lead to the conclusion that this behavior was not a security issue, because first it is coherent with the behavior with servers that do not support ALPN at all, and second it cannot make a tls-alpn-01 challenge be validated in this kind of corner case.
On top of the original modifications given by #5894, I merged the code to be up-to-date with our `master`, and fixed tests to match recent evolution about not displaying the `keyAuthorization` in the deserialized JSON form of an ACME challenge.
I also move the logic to verify if ALPN is available on the current system, and so that the tls-alpn-01 challenge can be used, to a dedicated static function `is_available` in `acme.challenge.TLSALPN01`. This function is used in the related tests to skip them, and will be used in the future from Certbot plugins to trigger or not the logic related to tls-alpn-01, depending on the OpenSSL version available to Python.
* Reimplement TLS-ALPN-01 challenge and standalone TLS-ALPN server from #5894.
* Setup a class method to check if tls-alpn-01 is supported.
* Add potential missing parameter in validation for tls-alpn
* Improve comments
* Make a class private
* Handle old versions of openssl that do not terminate the handshake when they should do.
* Add changelog
* Explicitly close the TLS connection by the book.
* Remove unused exception
* Fix lint
2020-03-12 16:53:19 -04:00
|
|
|
self.assertTrue(self.response.verify_cert(self.domain, cert))
|
|
|
|
|
|
|
|
|
|
def test_verify_bad_cert(self):
|
|
|
|
|
self.assertFalse(self.response.verify_cert(self.domain,
|
|
|
|
|
test_util.load_cert('cert.pem')))
|
|
|
|
|
|
|
|
|
|
def test_verify_bad_domain(self):
|
|
|
|
|
key1 = test_util.load_pyopenssl_private_key('rsa512_key.pem')
|
|
|
|
|
cert, key2 = self.response.gen_cert(self.domain, key1)
|
|
|
|
|
self.assertEqual(key1, key2)
|
|
|
|
|
self.assertFalse(self.response.verify_cert(self.domain2, cert))
|
|
|
|
|
|
|
|
|
|
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.challenges.TLSALPN01Response.verify_cert', autospec=True)
|
|
|
|
|
def test_simple_verify(self, mock_verify_cert):
|
|
|
|
|
mock_verify_cert.return_value = mock.sentinel.verification
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
mock.sentinel.verification, self.response.simple_verify(
|
|
|
|
|
self.chall, self.domain, KEY.public_key(),
|
|
|
|
|
cert=mock.sentinel.cert))
|
|
|
|
|
mock_verify_cert.assert_called_once_with(
|
|
|
|
|
self.response, self.domain, mock.sentinel.cert)
|
|
|
|
|
|
|
|
|
|
@mock.patch('acme.challenges.socket.gethostbyname')
|
|
|
|
|
@mock.patch('acme.challenges.crypto_util.probe_sni')
|
|
|
|
|
def test_probe_cert(self, mock_probe_sni, mock_gethostbyname):
|
|
|
|
|
mock_gethostbyname.return_value = '127.0.0.1'
|
|
|
|
|
self.response.probe_cert('foo.com')
|
|
|
|
|
mock_gethostbyname.assert_called_once_with('foo.com')
|
|
|
|
|
mock_probe_sni.assert_called_once_with(
|
2021-10-24 18:43:21 -04:00
|
|
|
host=b'127.0.0.1', port=self.response.PORT, name=b'foo.com',
|
Reimplement tls-alpn-01 in acme (#6886)
This PR is the first part of work described in #6724.
It reintroduces the tls-alpn-01 challenge in `acme` module, that was introduced by #5894 and reverted by #6100. The reason it was removed in the past is because some tests showed that with `1.0.2` branch of OpenSSL, the self-signed certificate containing the authorization key is sent to the requester even if the ALPN protocol `acme-tls/1` was not declared as supported by the requester during the TLS handshake.
However recent discussions lead to the conclusion that this behavior was not a security issue, because first it is coherent with the behavior with servers that do not support ALPN at all, and second it cannot make a tls-alpn-01 challenge be validated in this kind of corner case.
On top of the original modifications given by #5894, I merged the code to be up-to-date with our `master`, and fixed tests to match recent evolution about not displaying the `keyAuthorization` in the deserialized JSON form of an ACME challenge.
I also move the logic to verify if ALPN is available on the current system, and so that the tls-alpn-01 challenge can be used, to a dedicated static function `is_available` in `acme.challenge.TLSALPN01`. This function is used in the related tests to skip them, and will be used in the future from Certbot plugins to trigger or not the logic related to tls-alpn-01, depending on the OpenSSL version available to Python.
* Reimplement TLS-ALPN-01 challenge and standalone TLS-ALPN server from #5894.
* Setup a class method to check if tls-alpn-01 is supported.
* Add potential missing parameter in validation for tls-alpn
* Improve comments
* Make a class private
* Handle old versions of openssl that do not terminate the handshake when they should do.
* Add changelog
* Explicitly close the TLS connection by the book.
* Remove unused exception
* Fix lint
2020-03-12 16:53:19 -04:00
|
|
|
alpn_protocols=['acme-tls/1'])
|
|
|
|
|
|
|
|
|
|
self.response.probe_cert('foo.com', host='8.8.8.8')
|
|
|
|
|
mock_probe_sni.assert_called_with(
|
2021-10-24 18:43:21 -04:00
|
|
|
host=b'8.8.8.8', port=mock.ANY, name=b'foo.com',
|
Reimplement tls-alpn-01 in acme (#6886)
This PR is the first part of work described in #6724.
It reintroduces the tls-alpn-01 challenge in `acme` module, that was introduced by #5894 and reverted by #6100. The reason it was removed in the past is because some tests showed that with `1.0.2` branch of OpenSSL, the self-signed certificate containing the authorization key is sent to the requester even if the ALPN protocol `acme-tls/1` was not declared as supported by the requester during the TLS handshake.
However recent discussions lead to the conclusion that this behavior was not a security issue, because first it is coherent with the behavior with servers that do not support ALPN at all, and second it cannot make a tls-alpn-01 challenge be validated in this kind of corner case.
On top of the original modifications given by #5894, I merged the code to be up-to-date with our `master`, and fixed tests to match recent evolution about not displaying the `keyAuthorization` in the deserialized JSON form of an ACME challenge.
I also move the logic to verify if ALPN is available on the current system, and so that the tls-alpn-01 challenge can be used, to a dedicated static function `is_available` in `acme.challenge.TLSALPN01`. This function is used in the related tests to skip them, and will be used in the future from Certbot plugins to trigger or not the logic related to tls-alpn-01, depending on the OpenSSL version available to Python.
* Reimplement TLS-ALPN-01 challenge and standalone TLS-ALPN server from #5894.
* Setup a class method to check if tls-alpn-01 is supported.
* Add potential missing parameter in validation for tls-alpn
* Improve comments
* Make a class private
* Handle old versions of openssl that do not terminate the handshake when they should do.
* Add changelog
* Explicitly close the TLS connection by the book.
* Remove unused exception
* Fix lint
2020-03-12 16:53:19 -04:00
|
|
|
alpn_protocols=['acme-tls/1'])
|
|
|
|
|
|
|
|
|
|
@mock.patch('acme.challenges.TLSALPN01Response.probe_cert')
|
|
|
|
|
def test_simple_verify_false_on_probe_error(self, mock_probe_cert):
|
|
|
|
|
mock_probe_cert.side_effect = errors.Error
|
|
|
|
|
self.assertFalse(self.response.simple_verify(
|
|
|
|
|
self.chall, self.domain, KEY.public_key()))
|
|
|
|
|
|
2015-11-01 07:20:38 -05:00
|
|
|
|
2018-06-04 17:54:17 -04:00
|
|
|
class TLSALPN01Test(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
from acme.challenges import TLSALPN01
|
|
|
|
|
self.msg = TLSALPN01(
|
|
|
|
|
token=jose.b64decode('a82d5ff8ef740d12881f6d3c2277ab2e'))
|
|
|
|
|
self.jmsg = {
|
|
|
|
|
'type': 'tls-alpn-01',
|
|
|
|
|
'token': 'a82d5ff8ef740d12881f6d3c2277ab2e',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def test_to_partial_json(self):
|
|
|
|
|
self.assertEqual(self.jmsg, self.msg.to_partial_json())
|
|
|
|
|
|
|
|
|
|
def test_from_json(self):
|
|
|
|
|
from acme.challenges import TLSALPN01
|
|
|
|
|
self.assertEqual(self.msg, TLSALPN01.from_json(self.jmsg))
|
|
|
|
|
|
|
|
|
|
def test_from_json_hashable(self):
|
|
|
|
|
from acme.challenges import TLSALPN01
|
|
|
|
|
hash(TLSALPN01.from_json(self.jmsg))
|
|
|
|
|
|
|
|
|
|
def test_from_json_invalid_token_length(self):
|
|
|
|
|
from acme.challenges import TLSALPN01
|
|
|
|
|
self.jmsg['token'] = jose.encode_b64jose(b'abcd')
|
|
|
|
|
self.assertRaises(
|
|
|
|
|
jose.DeserializationError, TLSALPN01.from_json, self.jmsg)
|
|
|
|
|
|
Reimplement tls-alpn-01 in acme (#6886)
This PR is the first part of work described in #6724.
It reintroduces the tls-alpn-01 challenge in `acme` module, that was introduced by #5894 and reverted by #6100. The reason it was removed in the past is because some tests showed that with `1.0.2` branch of OpenSSL, the self-signed certificate containing the authorization key is sent to the requester even if the ALPN protocol `acme-tls/1` was not declared as supported by the requester during the TLS handshake.
However recent discussions lead to the conclusion that this behavior was not a security issue, because first it is coherent with the behavior with servers that do not support ALPN at all, and second it cannot make a tls-alpn-01 challenge be validated in this kind of corner case.
On top of the original modifications given by #5894, I merged the code to be up-to-date with our `master`, and fixed tests to match recent evolution about not displaying the `keyAuthorization` in the deserialized JSON form of an ACME challenge.
I also move the logic to verify if ALPN is available on the current system, and so that the tls-alpn-01 challenge can be used, to a dedicated static function `is_available` in `acme.challenge.TLSALPN01`. This function is used in the related tests to skip them, and will be used in the future from Certbot plugins to trigger or not the logic related to tls-alpn-01, depending on the OpenSSL version available to Python.
* Reimplement TLS-ALPN-01 challenge and standalone TLS-ALPN server from #5894.
* Setup a class method to check if tls-alpn-01 is supported.
* Add potential missing parameter in validation for tls-alpn
* Improve comments
* Make a class private
* Handle old versions of openssl that do not terminate the handshake when they should do.
* Add changelog
* Explicitly close the TLS connection by the book.
* Remove unused exception
* Fix lint
2020-03-12 16:53:19 -04:00
|
|
|
@mock.patch('acme.challenges.TLSALPN01Response.gen_cert')
|
|
|
|
|
def test_validation(self, mock_gen_cert):
|
|
|
|
|
mock_gen_cert.return_value = ('cert', 'key')
|
|
|
|
|
self.assertEqual(('cert', 'key'), self.msg.validation(
|
|
|
|
|
KEY, cert_key=mock.sentinel.cert_key, domain=mock.sentinel.domain))
|
|
|
|
|
mock_gen_cert.assert_called_once_with(key=mock.sentinel.cert_key,
|
|
|
|
|
domain=mock.sentinel.domain)
|
2018-06-04 17:54:17 -04:00
|
|
|
|
|
|
|
|
|
2015-02-12 03:45:03 -05:00
|
|
|
class DNSTest(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
2015-05-10 07:26:21 -04:00
|
|
|
from acme.challenges import DNS
|
2015-08-22 11:41:37 -04:00
|
|
|
self.msg = DNS(token=jose.b64decode(
|
|
|
|
|
b'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA'))
|
|
|
|
|
self.jmsg = {
|
|
|
|
|
'type': 'dns',
|
|
|
|
|
'token': 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA',
|
|
|
|
|
}
|
2015-02-12 03:45:03 -05:00
|
|
|
|
2015-04-18 04:11:28 -04:00
|
|
|
def test_to_partial_json(self):
|
|
|
|
|
self.assertEqual(self.jmsg, self.msg.to_partial_json())
|
2015-02-12 03:45:03 -05:00
|
|
|
|
|
|
|
|
def test_from_json(self):
|
2015-05-10 07:26:21 -04:00
|
|
|
from acme.challenges import DNS
|
2015-03-17 11:46:27 -04:00
|
|
|
self.assertEqual(self.msg, DNS.from_json(self.jmsg))
|
2015-02-12 03:45:03 -05:00
|
|
|
|
2015-04-18 01:48:32 -04:00
|
|
|
def test_from_json_hashable(self):
|
2015-05-10 07:26:21 -04:00
|
|
|
from acme.challenges import DNS
|
2015-04-18 01:48:32 -04:00
|
|
|
hash(DNS.from_json(self.jmsg))
|
|
|
|
|
|
2015-08-22 11:41:37 -04:00
|
|
|
def test_gen_check_validation(self):
|
2022-01-21 04:29:53 -05:00
|
|
|
ec_key_secp384r1 = JWKEC(key=test_util.load_ecdsa_private_key('ec_secp384r1_key.pem'))
|
|
|
|
|
for key, alg in [(KEY, jose.RS256), (ec_key_secp384r1, jose.ES384)]:
|
|
|
|
|
with self.subTest(key=key, alg=alg):
|
|
|
|
|
self.assertTrue(self.msg.check_validation(
|
|
|
|
|
self.msg.gen_validation(key, alg=alg), key.public_key()))
|
2015-08-22 11:41:37 -04:00
|
|
|
|
|
|
|
|
def test_gen_check_validation_wrong_key(self):
|
|
|
|
|
key2 = jose.JWKRSA.load(test_util.load_vector('rsa1024_key.pem'))
|
|
|
|
|
self.assertFalse(self.msg.check_validation(
|
2015-11-01 06:52:54 -05:00
|
|
|
self.msg.gen_validation(KEY), key2.public_key()))
|
2015-08-22 11:41:37 -04:00
|
|
|
|
|
|
|
|
def test_check_validation_wrong_payload(self):
|
|
|
|
|
validations = tuple(
|
2015-11-01 06:52:54 -05:00
|
|
|
jose.JWS.sign(payload=payload, alg=jose.RS256, key=KEY)
|
2015-08-22 11:41:37 -04:00
|
|
|
for payload in (b'', b'{}')
|
|
|
|
|
)
|
|
|
|
|
for validation in validations:
|
|
|
|
|
self.assertFalse(self.msg.check_validation(
|
2015-11-01 06:52:54 -05:00
|
|
|
validation, KEY.public_key()))
|
2015-08-22 11:41:37 -04:00
|
|
|
|
|
|
|
|
def test_check_validation_wrong_fields(self):
|
|
|
|
|
bad_validation = jose.JWS.sign(
|
2016-01-10 13:17:35 -05:00
|
|
|
payload=self.msg.update(
|
|
|
|
|
token=b'x' * 20).json_dumps().encode('utf-8'),
|
2015-11-01 06:52:54 -05:00
|
|
|
alg=jose.RS256, key=KEY)
|
2022-01-21 04:29:53 -05:00
|
|
|
self.assertFalse(self.msg.check_validation(bad_validation, KEY.public_key()))
|
2015-08-22 11:41:37 -04:00
|
|
|
|
|
|
|
|
def test_gen_response(self):
|
|
|
|
|
with mock.patch('acme.challenges.DNS.gen_validation') as mock_gen:
|
|
|
|
|
mock_gen.return_value = mock.sentinel.validation
|
2015-11-01 06:52:54 -05:00
|
|
|
response = self.msg.gen_response(KEY)
|
2015-08-22 11:41:37 -04:00
|
|
|
from acme.challenges import DNSResponse
|
2021-04-28 20:45:08 -04:00
|
|
|
self.assertIsInstance(response, DNSResponse)
|
2015-08-22 11:41:37 -04:00
|
|
|
self.assertEqual(response.validation, mock.sentinel.validation)
|
|
|
|
|
|
|
|
|
|
def test_validation_domain_name(self):
|
2022-01-21 04:29:53 -05:00
|
|
|
self.assertEqual('_acme-challenge.le.wtf', self.msg.validation_domain_name('le.wtf'))
|
|
|
|
|
|
|
|
|
|
def test_validation_domain_name_ecdsa(self):
|
|
|
|
|
ec_key_secp384r1 = JWKEC(key=test_util.load_ecdsa_private_key('ec_secp384r1_key.pem'))
|
|
|
|
|
self.assertIs(self.msg.check_validation(
|
|
|
|
|
self.msg.gen_validation(ec_key_secp384r1, alg=jose.ES384),
|
|
|
|
|
ec_key_secp384r1.public_key()), True
|
|
|
|
|
)
|
2015-08-22 11:41:37 -04:00
|
|
|
|
2015-02-12 03:45:03 -05:00
|
|
|
|
|
|
|
|
class DNSResponseTest(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
2015-08-22 11:41:37 -04:00
|
|
|
from acme.challenges import DNS
|
|
|
|
|
self.chall = DNS(token=jose.b64decode(
|
|
|
|
|
b"evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"))
|
|
|
|
|
self.validation = jose.JWS.sign(
|
|
|
|
|
payload=self.chall.json_dumps(sort_keys=True).encode(),
|
2015-10-28 15:42:06 -04:00
|
|
|
key=KEY, alg=jose.RS256)
|
2015-08-22 11:41:37 -04:00
|
|
|
|
2015-05-10 07:26:21 -04:00
|
|
|
from acme.challenges import DNSResponse
|
2015-08-22 11:41:37 -04:00
|
|
|
self.msg = DNSResponse(validation=self.validation)
|
|
|
|
|
self.jmsg_to = {
|
2015-07-17 10:58:06 -04:00
|
|
|
'resource': 'challenge',
|
|
|
|
|
'type': 'dns',
|
2015-08-22 11:41:37 -04:00
|
|
|
'validation': self.validation,
|
|
|
|
|
}
|
|
|
|
|
self.jmsg_from = {
|
|
|
|
|
'resource': 'challenge',
|
|
|
|
|
'type': 'dns',
|
|
|
|
|
'validation': self.validation.to_json(),
|
2015-07-17 10:58:06 -04:00
|
|
|
}
|
2015-02-12 03:45:03 -05:00
|
|
|
|
2015-04-18 04:11:28 -04:00
|
|
|
def test_to_partial_json(self):
|
2015-08-22 11:41:37 -04:00
|
|
|
self.assertEqual(self.jmsg_to, self.msg.to_partial_json())
|
2015-02-12 03:45:03 -05:00
|
|
|
|
|
|
|
|
def test_from_json(self):
|
2015-05-10 07:26:21 -04:00
|
|
|
from acme.challenges import DNSResponse
|
2015-08-22 11:41:37 -04:00
|
|
|
self.assertEqual(self.msg, DNSResponse.from_json(self.jmsg_from))
|
2015-02-12 03:45:03 -05:00
|
|
|
|
2015-04-18 01:48:32 -04:00
|
|
|
def test_from_json_hashable(self):
|
2015-05-10 07:26:21 -04:00
|
|
|
from acme.challenges import DNSResponse
|
2015-08-22 11:41:37 -04:00
|
|
|
hash(DNSResponse.from_json(self.jmsg_from))
|
|
|
|
|
|
|
|
|
|
def test_check_validation(self):
|
2022-01-21 04:29:53 -05:00
|
|
|
self.assertTrue(self.msg.check_validation(self.chall, KEY.public_key()))
|
2015-04-18 01:48:32 -04:00
|
|
|
|
2015-02-12 03:45:03 -05:00
|
|
|
|
2020-03-13 12:56:35 -04:00
|
|
|
class JWSPayloadRFC8555Compliant(unittest.TestCase):
|
|
|
|
|
"""Test for RFC8555 compliance of JWS generated from resources/challenges"""
|
|
|
|
|
def test_challenge_payload(self):
|
|
|
|
|
from acme.challenges import HTTP01Response
|
|
|
|
|
|
|
|
|
|
challenge_body = HTTP01Response()
|
|
|
|
|
challenge_body.le_acme_version = 2
|
|
|
|
|
|
|
|
|
|
jobj = challenge_body.json_dumps(indent=2).encode()
|
|
|
|
|
# RFC8555 states that challenge responses must have an empty payload.
|
|
|
|
|
self.assertEqual(jobj, b'{}')
|
|
|
|
|
|
|
|
|
|
|
2015-02-12 03:45:03 -05:00
|
|
|
if __name__ == '__main__':
|
2015-05-10 13:34:25 -04:00
|
|
|
unittest.main() # pragma: no cover
|