Merge pull request #1411 from kuba/tls-sni-01

tls-sni-01
This commit is contained in:
Peter Eckersley 2015-11-07 22:42:49 -08:00
commit d209b5ea20
39 changed files with 403 additions and 515 deletions

View file

@ -187,7 +187,7 @@ class KeyAuthorizationChallenge(_TokenDVChallenge):
key_authorization=self.key_authorization(account_key))
@abc.abstractmethod
def validation(self, account_key):
def validation(self, account_key, **kwargs):
"""Generate validation for the challenge.
Subclasses must implement this method, but they are likely to
@ -201,7 +201,7 @@ class KeyAuthorizationChallenge(_TokenDVChallenge):
"""
raise NotImplementedError() # pragma: no cover
def response_and_validation(self, account_key):
def response_and_validation(self, account_key, *args, **kwargs):
"""Generate response and validation.
Convenience function that return results of `response` and
@ -211,7 +211,8 @@ class KeyAuthorizationChallenge(_TokenDVChallenge):
:rtype: tuple
"""
return (self.response(account_key), self.validation(account_key))
return (self.response(account_key),
self.validation(account_key, *args, **kwargs))
@ChallengeResponse.register
@ -220,6 +221,12 @@ class HTTP01Response(KeyAuthorizationChallengeResponse):
typ = "http-01"
PORT = 80
"""Verification port as defined by the protocol.
You can override it (e.g. for testing) by passing ``port`` to
`simple_verify`.
"""
def simple_verify(self, chall, domain, account_public_key, port=None):
"""Simple verify.
@ -246,7 +253,7 @@ class HTTP01Response(KeyAuthorizationChallengeResponse):
# request URI, if it's standard.
if port is not None and port != self.PORT:
logger.warning(
"Using non-standard port for SimpleHTTP verification: %s", port)
"Using non-standard port for http-01 verification: %s", port)
domain += ":{0}".format(port)
uri = chall.uri(domain)
@ -308,7 +315,7 @@ class HTTP01(KeyAuthorizationChallenge):
"""
return "http://" + domain + self.path
def validation(self, account_key):
def validation(self, account_key, **unused_kwargs):
"""Generate validation.
:param JWK account_key:
@ -318,89 +325,50 @@ class HTTP01(KeyAuthorizationChallenge):
return self.key_authorization(account_key)
@Challenge.register # pylint: disable=too-many-ancestors
class DVSNI(_TokenDVChallenge):
"""ACME "dvsni" challenge.
:ivar bytes token: Random data, **not** base64-encoded.
"""
typ = "dvsni"
PORT = 443
"""Port to perform DVSNI challenge."""
def gen_response(self, account_key, alg=jose.RS256, **kwargs):
"""Generate response.
:param .JWK account_key: Private account key.
:rtype: .DVSNIResponse
"""
return DVSNIResponse(validation=jose.JWS.sign(
payload=self.json_dumps(sort_keys=True).encode('utf-8'),
key=account_key, alg=alg, **kwargs))
@ChallengeResponse.register
class DVSNIResponse(ChallengeResponse):
"""ACME "dvsni" challenge response.
:param bytes s: Random data, **not** base64-encoded.
"""
typ = "dvsni"
class TLSSNI01Response(KeyAuthorizationChallengeResponse):
"""ACME tls-sni-01 challenge response."""
typ = "tls-sni-01"
DOMAIN_SUFFIX = b".acme.invalid"
"""Domain name suffix."""
PORT = DVSNI.PORT
"""Port to perform DVSNI challenge."""
PORT = 443
"""Verification port as defined by the protocol.
validation = jose.Field("validation", decoder=jose.JWS.from_json)
You can override it (e.g. for testing) by passing ``port`` to
`simple_verify`.
"""
@property
def z(self): # pylint: disable=invalid-name
"""The ``z`` parameter.
def z(self):
"""``z`` value used for verification.
:rtype: bytes
:rtype bytes:
"""
# Instance of 'Field' has no 'signature' member
# pylint: disable=no-member
return hashlib.sha256(self.validation.signature.encode(
"signature").encode("utf-8")).hexdigest().encode()
return hashlib.sha256(
self.key_authorization.encode("utf-8")).hexdigest().lower().encode()
@property
def z_domain(self):
"""Domain name for certificate subjectAltName.
"""Domain name used for verification, generated from `z`.
:rtype: bytes
:rtype bytes:
"""
z = self.z # pylint: disable=invalid-name
return z[:32] + b'.' + z[32:] + self.DOMAIN_SUFFIX
@property
def chall(self):
"""Get challenge encoded in the `validation` payload.
:rtype: challenges.DVSNI
"""
# pylint: disable=no-member
return DVSNI.json_loads(self.validation.payload.decode('utf-8'))
return self.z[:32] + b'.' + self.z[32:] + self.DOMAIN_SUFFIX
def gen_cert(self, key=None, bits=2048):
"""Generate DVSNI certificate.
"""Generate tls-sni-01 certificate.
:param OpenSSL.crypto.PKey key: Optional private key used in
certificate generation. If not provided (``None``), then
fresh key will be generated.
:param int bits: Number of bits for newly generated key.
:rtype: `tuple` of `OpenSSL.crypto.X509` and
`OpenSSL.crypto.PKey`
:rtype: `tuple` of `OpenSSL.crypto.X509` and `OpenSSL.crypto.PKey`
"""
if key is None:
@ -411,11 +379,12 @@ class DVSNIResponse(ChallengeResponse):
'dummy', self.z_domain.decode()], force_san=True), key
def probe_cert(self, domain, **kwargs):
"""Probe DVSNI challenge certificate.
"""Probe tls-sni-01 challenge certificate.
:param unicode domain:
"""
# TODO: domain is not necessary if host is provided
if "host" not in kwargs:
host = socket.gethostbyname(domain)
logging.debug('%s resolved to %s', domain, host)
@ -428,7 +397,7 @@ class DVSNIResponse(ChallengeResponse):
return crypto_util.probe_sni(**kwargs)
def verify_cert(self, cert):
"""Verify DVSNI challenge certificate."""
"""Verify tls-sni-01 challenge certificate."""
# pylint: disable=protected-access
sans = crypto_util._pyopenssl_cert_or_req_san(cert)
logging.debug('Certificate %s. SANs: %s', cert.digest('sha1'), sans)
@ -439,14 +408,15 @@ class DVSNIResponse(ChallengeResponse):
"""Simple verify.
Verify ``validation`` using ``account_public_key``, optionally
probe DVSNI certificate and check using `verify_cert`.
probe tls-sni-01 certificate and check using `verify_cert`.
:param .challenges.DVSNI chall: Corresponding challenge.
:param .challenges.TLSSNI01 chall: Corresponding challenge.
:param str domain: Domain name being validated.
:param JWK account_public_key:
:param OpenSSL.crypto.X509 cert: Optional certificate. If not
provided (``None``) certificate will be retrieved using
`probe_cert`.
:param int port: Port used to probe the certificate.
:returns: ``True`` iff client's control of the domain has been
@ -454,20 +424,8 @@ class DVSNIResponse(ChallengeResponse):
:rtype: bool
"""
# pylint: disable=no-member
if not self.validation.verify(key=account_public_key):
return False
# TODO: it's not checked that payload has exectly 2 fields!
try:
decoded_chall = self.chall
except jose.DeserializationError as error:
logger.debug(error, exc_info=True)
return False
if decoded_chall.token != chall.token:
logger.debug("Wrong token: expected %r, found %r",
chall.token, decoded_chall.token)
if not self.verify(chall, account_public_key):
logger.debug("Verification of key authorization in response failed")
return False
if cert is None:
@ -480,6 +438,29 @@ class DVSNIResponse(ChallengeResponse):
return self.verify_cert(cert)
@Challenge.register # pylint: disable=too-many-ancestors
class TLSSNI01(KeyAuthorizationChallenge):
"""ACME tls-sni-01 challenge."""
response_cls = TLSSNI01Response
typ = response_cls.typ
# boulder#962, ietf-wg-acme#22
#n = jose.Field("n", encoder=int, decoder=int)
def validation(self, account_key, **kwargs):
"""Generate validation.
:param JWK account_key:
:param OpenSSL.crypto.PKey cert_key: Optional private key used
in certificate generation. If not provided (``None``), then
fresh key will be generated.
:rtype: `tuple` of `OpenSSL.crypto.X509` and `OpenSSL.crypto.PKey`
"""
return self.response(account_key).gen_cert(key=kwargs.get('cert_key'))
@Challenge.register
class RecoveryContact(ContinuityChallenge):
"""ACME "recoveryContact" challenge.

View file

@ -186,14 +186,112 @@ class HTTP01Test(unittest.TestCase):
self.msg.update(token=b'..').good_token)
class DVSNITest(unittest.TestCase):
class TLSSNI01ResponseTest(unittest.TestCase):
# pylint: disable=too-many-instance-attributes
def setUp(self):
from acme.challenges import DVSNI
self.msg = DVSNI(
from acme.challenges import TLSSNI01
self.chall = TLSSNI01(
token=jose.b64decode(b'a82d5ff8ef740d12881f6d3c2277ab2e'))
self.response = self.chall.response(KEY)
self.jmsg = {
'resource': 'challenge',
'type': 'tls-sni-01',
'keyAuthorization': self.response.key_authorization,
}
# pylint: disable=invalid-name
label1 = b'dc38d9c3fa1a4fdcc3a5501f2d38583f'
label2 = b'b7793728f084394f2a1afd459556bb5c'
self.z = label1 + label2
self.z_domain = label1 + b'.' + label2 + b'.acme.invalid'
self.domain = 'foo.com'
def test_z_and_domain(self):
self.assertEqual(self.z, self.response.z)
self.assertEqual(self.z_domain, self.response.z_domain)
def test_to_partial_json(self):
self.assertEqual(self.jmsg, self.response.to_partial_json())
def test_from_json(self):
from acme.challenges import TLSSNI01Response
self.assertEqual(self.response, TLSSNI01Response.from_json(self.jmsg))
def test_from_json_hashable(self):
from acme.challenges import TLSSNI01Response
hash(TLSSNI01Response.from_json(self.jmsg))
@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(
host='127.0.0.1', port=self.response.PORT,
name=self.z_domain)
self.response.probe_cert('foo.com', host='8.8.8.8')
mock_probe_sni.assert_called_with(
host='8.8.8.8', port=mock.ANY, name=mock.ANY)
self.response.probe_cert('foo.com', port=1234)
mock_probe_sni.assert_called_with(
host=mock.ANY, port=1234, name=mock.ANY)
self.response.probe_cert('foo.com', bar='baz')
mock_probe_sni.assert_called_with(
host=mock.ANY, port=mock.ANY, name=mock.ANY, bar='baz')
self.response.probe_cert('foo.com', name=b'xxx')
mock_probe_sni.assert_called_with(
host=mock.ANY, port=mock.ANY,
name=self.z_domain)
def test_gen_verify_cert(self):
key1 = test_util.load_pyopenssl_private_key('rsa512_key.pem')
cert, key2 = self.response.gen_cert(key1)
self.assertEqual(key1, key2)
self.assertTrue(self.response.verify_cert(cert))
def test_gen_verify_cert_gen_key(self):
cert, key = self.response.gen_cert()
self.assertTrue(isinstance(key, OpenSSL.crypto.PKey))
self.assertTrue(self.response.verify_cert(cert))
def test_verify_bad_cert(self):
self.assertFalse(self.response.verify_cert(
test_util.load_cert('cert.pem')))
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.TLSSNI01Response.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, mock.sentinel.cert)
@mock.patch('acme.challenges.TLSSNI01Response.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()))
class TLSSNI01Test(unittest.TestCase):
def setUp(self):
from acme.challenges import TLSSNI01
self.msg = TLSSNI01(
token=jose.b64decode('a82d5ff8ef740d12881f6d3c2277ab2e'))
self.jmsg = {
'type': 'dvsni',
'type': 'tls-sni-01',
'token': 'a82d5ff8ef740d12881f6d3c2277ab2e',
}
@ -201,144 +299,25 @@ class DVSNITest(unittest.TestCase):
self.assertEqual(self.jmsg, self.msg.to_partial_json())
def test_from_json(self):
from acme.challenges import DVSNI
self.assertEqual(self.msg, DVSNI.from_json(self.jmsg))
from acme.challenges import TLSSNI01
self.assertEqual(self.msg, TLSSNI01.from_json(self.jmsg))
def test_from_json_hashable(self):
from acme.challenges import DVSNI
hash(DVSNI.from_json(self.jmsg))
from acme.challenges import TLSSNI01
hash(TLSSNI01.from_json(self.jmsg))
def test_from_json_invalid_token_length(self):
from acme.challenges import DVSNI
from acme.challenges import TLSSNI01
self.jmsg['token'] = jose.encode_b64jose(b'abcd')
self.assertRaises(
jose.DeserializationError, DVSNI.from_json, self.jmsg)
jose.DeserializationError, TLSSNI01.from_json, self.jmsg)
def test_gen_response(self):
from acme.challenges import DVSNI
self.assertEqual(self.msg, DVSNI.json_loads(
self.msg.gen_response(KEY).validation.payload.decode()))
class DVSNIResponseTest(unittest.TestCase):
# pylint: disable=too-many-instance-attributes
def setUp(self):
from acme.challenges import DVSNI
self.chall = DVSNI(
token=jose.b64decode(b'a82d5ff8ef740d12881f6d3c2277ab2e'))
from acme.challenges import DVSNIResponse
self.validation = jose.JWS.sign(
payload=self.chall.json_dumps(sort_keys=True).encode(),
key=KEY, alg=jose.RS256)
self.msg = DVSNIResponse(validation=self.validation)
self.jmsg_to = {
'resource': 'challenge',
'type': 'dvsni',
'validation': self.validation,
}
self.jmsg_from = {
'resource': 'challenge',
'type': 'dvsni',
'validation': self.validation.to_json(),
}
# pylint: disable=invalid-name
label1 = b'e2df3498860637c667fedadc5a8494ec'
label2 = b'09dcc75553c9b3bd73662b50e71b1e42'
self.z = label1 + label2
self.z_domain = label1 + b'.' + label2 + b'.acme.invalid'
self.domain = 'foo.com'
def test_z_and_domain(self):
self.assertEqual(self.z, self.msg.z)
self.assertEqual(self.z_domain, self.msg.z_domain)
def test_to_partial_json(self):
self.assertEqual(self.jmsg_to, self.msg.to_partial_json())
def test_from_json(self):
from acme.challenges import DVSNIResponse
self.assertEqual(self.msg, DVSNIResponse.from_json(self.jmsg_from))
def test_from_json_hashable(self):
from acme.challenges import DVSNIResponse
hash(DVSNIResponse.from_json(self.jmsg_from))
@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.msg.probe_cert('foo.com')
mock_gethostbyname.assert_called_once_with('foo.com')
mock_probe_sni.assert_called_once_with(
host='127.0.0.1', port=self.msg.PORT,
name=self.z_domain)
self.msg.probe_cert('foo.com', host='8.8.8.8')
mock_probe_sni.assert_called_with(
host='8.8.8.8', port=mock.ANY, name=mock.ANY)
self.msg.probe_cert('foo.com', port=1234)
mock_probe_sni.assert_called_with(
host=mock.ANY, port=1234, name=mock.ANY)
self.msg.probe_cert('foo.com', bar='baz')
mock_probe_sni.assert_called_with(
host=mock.ANY, port=mock.ANY, name=mock.ANY, bar='baz')
self.msg.probe_cert('foo.com', name=b'xxx')
mock_probe_sni.assert_called_with(
host=mock.ANY, port=mock.ANY,
name=self.z_domain)
def test_gen_verify_cert(self):
key1 = test_util.load_pyopenssl_private_key('rsa512_key.pem')
cert, key2 = self.msg.gen_cert(key1)
self.assertEqual(key1, key2)
self.assertTrue(self.msg.verify_cert(cert))
def test_gen_verify_cert_gen_key(self):
cert, key = self.msg.gen_cert()
self.assertTrue(isinstance(key, OpenSSL.crypto.PKey))
self.assertTrue(self.msg.verify_cert(cert))
def test_verify_bad_cert(self):
self.assertFalse(self.msg.verify_cert(test_util.load_cert('cert.pem')))
def test_simple_verify_wrong_account_key(self):
self.assertFalse(self.msg.simple_verify(
self.chall, self.domain, jose.JWKRSA.load(
test_util.load_vector('rsa256_key.pem')).public_key()))
def test_simple_verify_wrong_payload(self):
for payload in b'', b'{}':
msg = self.msg.update(validation=jose.JWS.sign(
payload=payload, key=KEY, alg=jose.RS256))
self.assertFalse(msg.simple_verify(
self.chall, self.domain, KEY.public_key()))
def test_simple_verify_wrong_token(self):
msg = self.msg.update(validation=jose.JWS.sign(
payload=self.chall.update(token=(b'b' * 20)).json_dumps().encode(),
key=KEY, alg=jose.RS256))
self.assertFalse(msg.simple_verify(
self.chall, self.domain, KEY.public_key()))
@mock.patch('acme.challenges.DVSNIResponse.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.msg.simple_verify(
self.chall, self.domain, KEY.public_key(),
cert=mock.sentinel.cert))
mock_verify_cert.assert_called_once_with(self.msg, mock.sentinel.cert)
@mock.patch('acme.challenges.DVSNIResponse.probe_cert')
def test_simple_verify_false_on_probe_error(self, mock_probe_cert):
mock_probe_cert.side_effect = errors.Error
self.assertFalse(self.msg.simple_verify(
self.chall, self.domain, KEY.public_key()))
@mock.patch('acme.challenges.TLSSNI01Response.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))
mock_gen_cert.assert_called_once_with(key=mock.sentinel.cert_key)
class RecoveryContactTest(unittest.TestCase):
@ -571,8 +550,6 @@ class ProofOfPossessionResponseTest(unittest.TestCase):
class DNSTest(unittest.TestCase):
def setUp(self):
self.account_key = jose.JWKRSA.load(
test_util.load_vector('rsa512_key.pem'))
from acme.challenges import DNS
self.msg = DNS(token=jose.b64decode(
b'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA'))
@ -594,34 +571,33 @@ class DNSTest(unittest.TestCase):
def test_gen_check_validation(self):
self.assertTrue(self.msg.check_validation(
self.msg.gen_validation(self.account_key),
self.account_key.public_key()))
self.msg.gen_validation(KEY), KEY.public_key()))
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(
self.msg.gen_validation(self.account_key), key2.public_key()))
self.msg.gen_validation(KEY), key2.public_key()))
def test_check_validation_wrong_payload(self):
validations = tuple(
jose.JWS.sign(payload=payload, alg=jose.RS256, key=self.account_key)
jose.JWS.sign(payload=payload, alg=jose.RS256, key=KEY)
for payload in (b'', b'{}')
)
for validation in validations:
self.assertFalse(self.msg.check_validation(
validation, self.account_key.public_key()))
validation, KEY.public_key()))
def test_check_validation_wrong_fields(self):
bad_validation = jose.JWS.sign(
payload=self.msg.update(token=b'x' * 20).json_dumps().encode('utf-8'),
alg=jose.RS256, key=self.account_key)
alg=jose.RS256, key=KEY)
self.assertFalse(self.msg.check_validation(
bad_validation, self.account_key.public_key()))
bad_validation, KEY.public_key()))
def test_gen_response(self):
with mock.patch('acme.challenges.DNS.gen_validation') as mock_gen:
mock_gen.return_value = mock.sentinel.validation
response = self.msg.gen_response(self.account_key)
response = self.msg.gen_response(KEY)
from acme.challenges import DNSResponse
self.assertTrue(isinstance(response, DNSResponse))
self.assertEqual(response.validation, mock.sentinel.validation)

View file

@ -13,7 +13,7 @@ from acme import errors
logger = logging.getLogger(__name__)
# DVSNI certificate serving and probing is not affected by SSL
# TLSSNI01 certificate serving and probing is not affected by SSL
# vulnerabilities: prober needs to check certificate for expected
# contents anyway. Working SNI is the only thing that's necessary for
# the challenge and thus scoping down SSL/TLS method (version) would
@ -23,7 +23,7 @@ logger = logging.getLogger(__name__)
# https://www.openssl.org/docs/ssl/SSLv23_method.html). _serve_sni
# should be changed to use "set_options" to disable SSLv2 and SSLv3,
# in case it's used for things other than probing/serving!
_DEFAULT_DVSNI_SSL_METHOD = OpenSSL.SSL.SSLv23_METHOD
_DEFAULT_TLSSNI01_SSL_METHOD = OpenSSL.SSL.SSLv23_METHOD
class SSLSocket(object): # pylint: disable=too-few-public-methods
@ -35,7 +35,7 @@ class SSLSocket(object): # pylint: disable=too-few-public-methods
:ivar method: See `OpenSSL.SSL.Context` for allowed values.
"""
def __init__(self, sock, certs, method=_DEFAULT_DVSNI_SSL_METHOD):
def __init__(self, sock, certs, method=_DEFAULT_TLSSNI01_SSL_METHOD):
self.sock = sock
self.certs = certs
self.method = method
@ -103,7 +103,7 @@ class SSLSocket(object): # pylint: disable=too-few-public-methods
def probe_sni(name, host, port=443, timeout=300,
method=_DEFAULT_DVSNI_SSL_METHOD, source_address=('0', 0)):
method=_DEFAULT_TLSSNI01_SSL_METHOD, source_address=('0', 0)):
"""Probe SNI server for SSL certificate.
:param bytes name: Byte string to send as the server name in the

View file

@ -30,7 +30,7 @@ class TLSServer(socketserver.TCPServer):
self.certs = kwargs.pop("certs", {})
self.method = kwargs.pop(
# pylint: disable=protected-access
"method", crypto_util._DEFAULT_DVSNI_SSL_METHOD)
"method", crypto_util._DEFAULT_TLSSNI01_SSL_METHOD)
self.allow_reuse_address = kwargs.pop("allow_reuse_address", True)
socketserver.TCPServer.__init__(self, *args, **kwargs)
@ -50,8 +50,8 @@ class ACMEServerMixin: # pylint: disable=old-style-class
allow_reuse_address = True
class DVSNIServer(TLSServer, ACMEServerMixin):
"""DVSNI Server."""
class TLSSNI01Server(TLSServer, ACMEServerMixin):
"""TLSSNI01 Server."""
def __init__(self, server_address, certs):
TLSServer.__init__(
@ -134,8 +134,8 @@ class HTTP01RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
cls, simple_http_resources=simple_http_resources)
def simple_dvsni_server(cli_args, forever=True):
"""Run simple standalone DVSNI server."""
def simple_tls_sni_01_server(cli_args, forever=True):
"""Run simple standalone TLSSNI01 server."""
logging.basicConfig(level=logging.DEBUG)
parser = argparse.ArgumentParser()
@ -158,7 +158,7 @@ def simple_dvsni_server(cli_args, forever=True):
OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert_contents))
server = DVSNIServer(('', int(args.port)), certs=certs)
server = TLSSNI01Server(('', int(args.port)), certs=certs)
six.print_("Serving at https://localhost:{0}...".format(
server.socket.getsockname()[1]))
if forever: # pragma: no cover
@ -168,4 +168,4 @@ def simple_dvsni_server(cli_args, forever=True):
if __name__ == "__main__":
sys.exit(simple_dvsni_server(sys.argv)) # pragma: no cover
sys.exit(simple_tls_sni_01_server(sys.argv)) # pragma: no cover

View file

@ -28,8 +28,8 @@ class TLSServerTest(unittest.TestCase):
server.server_close() # pylint: disable=no-member
class DVSNIServerTest(unittest.TestCase):
"""Test for acme.standalone.DVSNIServer."""
class TLSSNI01ServerTest(unittest.TestCase):
"""Test for acme.standalone.TLSSNI01Server."""
def setUp(self):
self.certs = {
@ -37,8 +37,8 @@ class DVSNIServerTest(unittest.TestCase):
# pylint: disable=protected-access
test_util.load_cert('cert.pem')._wrapped),
}
from acme.standalone import DVSNIServer
self.server = DVSNIServer(("", 0), certs=self.certs)
from acme.standalone import TLSSNI01Server
self.server = TLSSNI01Server(("", 0), certs=self.certs)
# pylint: disable=no-member
self.thread = threading.Thread(target=self.server.serve_forever)
self.thread.start()
@ -106,8 +106,8 @@ class HTTP01ServerTest(unittest.TestCase):
self.assertFalse(self._test_http01(add=False))
class TestSimpleDVSNIServer(unittest.TestCase):
"""Tests for acme.standalone.simple_dvsni_server."""
class TestSimpleTLSSNI01Server(unittest.TestCase):
"""Tests for acme.standalone.simple_tls_sni_01_server."""
def setUp(self):
# mirror ../examples/standalone
@ -118,12 +118,14 @@ class TestSimpleDVSNIServer(unittest.TestCase):
shutil.copy(test_util.vector_path('rsa512_key.pem'),
os.path.join(localhost_dir, 'key.pem'))
from acme.standalone import simple_dvsni_server
from acme.standalone import simple_tls_sni_01_server
self.port = 1234
self.thread = threading.Thread(target=simple_dvsni_server, kwargs={
'cli_args': ('xxx', '--port', str(self.port)),
'forever': False,
})
self.thread = threading.Thread(
target=simple_tls_sni_01_server, kwargs={
'cli_args': ('xxx', '--port', str(self.port)),
'forever': False,
},
)
self.old_cwd = os.getcwd()
os.chdir(self.test_cwd)
self.thread.start()

View file

@ -151,7 +151,7 @@ certificate for some domain name by solving challenges received from
the ACME server. From the protocol, there are essentially two
different types of challenges. Challenges that must be solved by
individual plugins in order to satisfy domain validation (subclasses
of `~.DVChallenge`, i.e. `~.challenges.DVSNI`,
of `~.DVChallenge`, i.e. `~.challenges.TLSSNI01`,
`~.challenges.HTTP01`, `~.challenges.DNS`) and continuity specific
challenges (subclasses of `~.ContinuityChallenge`,
i.e. `~.challenges.RecoveryToken`, `~.challenges.RecoveryContact`,
@ -160,7 +160,7 @@ always handled by the `~.ContinuityAuthenticator`, while plugins are
expected to handle `~.DVChallenge` types.
Right now, we have two authenticator plugins, the `~.ApacheConfigurator`
and the `~.StandaloneAuthenticator`. The Standalone and Apache
authenticators only solve the `~.challenges.DVSNI` challenge currently.
authenticators only solve the `~.challenges.TLSSNI01` challenge currently.
(You can set which challenges your authenticator can handle through the
:meth:`~.IAuthenticator.get_chall_pref`.

View file

@ -135,7 +135,7 @@ Plugin A I Notes and status
========== = = ================================================================
standalone Y N Very stable. Uses port 80 (force by
``--standalone-supported-challenges http-01``) or 443
(force by ``--standalone-supported-challenges dvsni``).
(force by ``--standalone-supported-challenges tls-sni-01``).
apache Y Y Alpha. Automates Apache installation, works fairly well but on
Debian-based distributions only for now.
webroot Y N Works with already running webserver, by writing necessary files

View file

@ -16,7 +16,7 @@ server = https://acme-staging.api.letsencrypt.org/directory
# Uncomment to use the standalone authenticator on port 443
# authenticator = standalone
# standalone-supported-challenges = dvsni
# standalone-supported-challenges = tls-sni-01
# Uncomment to use the webroot authenticator. Replace webroot-path with the
# path to the public_html / webroot folder being served by your web server.

View file

@ -13,7 +13,6 @@ import zope.interface
from acme import challenges
from letsencrypt import achallenges
from letsencrypt import errors
from letsencrypt import interfaces
from letsencrypt import le_util
@ -1117,7 +1116,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
###########################################################################
def get_chall_pref(self, unused_domain): # pylint: disable=no-self-use
"""Return list of challenge preferences."""
return [challenges.DVSNI]
return [challenges.TLSSNI01]
def perform(self, achalls):
"""Perform the configuration related challenge.
@ -1132,11 +1131,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
apache_dvsni = dvsni.ApacheDvsni(self)
for i, achall in enumerate(achalls):
if isinstance(achall, achallenges.DVSNI):
# Currently also have dvsni hold associated index
# of the challenge. This helps to put all of the responses back
# together when they are all complete.
apache_dvsni.add_chall(achall, i)
# Currently also have dvsni hold associated index
# of the challenge. This helps to put all of the responses back
# together when they are all complete.
apache_dvsni.add_chall(achall, i)
sni_response = apache_dvsni.perform()
if sni_response:

View file

@ -7,14 +7,14 @@ from letsencrypt_apache import obj
from letsencrypt_apache import parser
class ApacheDvsni(common.Dvsni):
class ApacheDvsni(common.TLSSNI01):
"""Class performs DVSNI challenges within the Apache configurator.
:ivar configurator: ApacheConfigurator object
:type configurator: :class:`~apache.configurator.ApacheConfigurator`
:ivar list achalls: Annotated :class:`~letsencrypt.achallenges.DVSNI`
challenges.
:ivar list achalls: Annotated tls-sni-01
(`.KeyAuthorizationAnnotatedChallenge`) challenges.
:param list indices: Meant to hold indices of challenges in a
larger array. ApacheDvsni is capable of solving many challenges
@ -62,7 +62,7 @@ class ApacheDvsni(common.Dvsni):
# Prepare the server for HTTPS
self.configurator.prepare_server_https(
str(self.configurator.config.dvsni_port), True)
str(self.configurator.config.tls_sni_01_port), True)
responses = []
@ -114,14 +114,15 @@ class ApacheDvsni(common.Dvsni):
# TODO: Checkout _default_ rules.
dvsni_addrs = set()
default_addr = obj.Addr(("*", str(self.configurator.config.dvsni_port)))
default_addr = obj.Addr(("*", str(
self.configurator.config.tls_sni_01_port)))
for addr in vhost.addrs:
if "_default_" == addr.get_addr():
dvsni_addrs.add(default_addr)
else:
dvsni_addrs.add(
addr.get_sni_addr(self.configurator.config.dvsni_port))
addr.get_sni_addr(self.configurator.config.tls_sni_01_port))
return dvsni_addrs
@ -144,8 +145,8 @@ class ApacheDvsni(common.Dvsni):
def _get_config_text(self, achall, ip_addrs):
"""Chocolate virtual server configuration text
:param achall: Annotated DVSNI challenge.
:type achall: :class:`letsencrypt.achallenges.DVSNI`
:param .KeyAuthorizationAnnotatedChallenge achall: Annotated
DVSNI challenge.
:param list ip_addrs: addresses of challenged domain
:class:`list` of type `~.obj.Addr`
@ -164,7 +165,7 @@ class ApacheDvsni(common.Dvsni):
# https://docs.python.org/2.7/reference/lexical_analysis.html
return self.VHOST_TEMPLATE.format(
vhost=ips,
server_name=achall.gen_response(achall.account_key).z_domain,
server_name=achall.response(achall.account_key).z_domain,
ssl_options_conf_path=self.configurator.mod_ssl_conf,
cert_path=self.get_cert_path(achall),
key_path=self.get_key_path(achall),

View file

@ -382,8 +382,8 @@ class TwoVhost80Test(util.ApacheTest):
account_key, achall1, achall2 = self.get_achalls()
dvsni_ret_val = [
achall1.gen_response(account_key),
achall2.gen_response(account_key),
achall1.response(account_key),
achall2.response(account_key),
]
mock_dvsni_perform.return_value = dvsni_ret_val
@ -592,15 +592,15 @@ class TwoVhost80Test(util.ApacheTest):
def get_achalls(self):
"""Return testing achallenges."""
account_key = self.rsa512jwk
achall1 = achallenges.DVSNI(
achall1 = achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.chall_to_challb(
challenges.DVSNI(
challenges.TLSSNI01(
token="jIq_Xy1mXGN37tb4L6Xj_es58fW571ZNyXekdZzhh7Q"),
"pending"),
domain="encryption-example.demo", account_key=account_key)
achall2 = achallenges.DVSNI(
achall2 = achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.chall_to_challb(
challenges.DVSNI(
challenges.TLSSNI01(
token="uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU"),
"pending"),
domain="letsencrypt.demo", account_key=account_key)

View file

@ -13,15 +13,15 @@ from letsencrypt_apache.tests import util
class DvsniPerformTest(util.ApacheTest):
"""Test the ApacheDVSNI challenge."""
auth_key = common_test.DvsniTest.auth_key
achalls = common_test.DvsniTest.achalls
auth_key = common_test.TLSSNI01Test.auth_key
achalls = common_test.TLSSNI01Test.achalls
def setUp(self): # pylint: disable=arguments-differ
super(DvsniPerformTest, self).setUp()
config = util.get_apache_configurator(
self.config_path, self.config_dir, self.work_dir)
config.config.dvsni_port = 443
config.config.tls_sni_01_port = 443
from letsencrypt_apache import dvsni
self.sni = dvsni.ApacheDvsni(config)
@ -46,7 +46,7 @@ class DvsniPerformTest(util.ApacheTest):
achall = self.achalls[0]
self.sni.add_chall(achall)
response = self.achalls[0].gen_response(self.auth_key)
response = self.achalls[0].response(self.auth_key)
mock_setup_cert = mock.MagicMock(return_value=response)
# pylint: disable=protected-access
self.sni._setup_challenge_cert = mock_setup_cert
@ -72,7 +72,7 @@ class DvsniPerformTest(util.ApacheTest):
acme_responses = []
for achall in self.achalls:
self.sni.add_chall(achall)
acme_responses.append(achall.gen_response(self.auth_key))
acme_responses.append(achall.response(self.auth_key))
mock_setup_cert = mock.MagicMock(side_effect=acme_responses)
# pylint: disable=protected-access
@ -100,7 +100,7 @@ class DvsniPerformTest(util.ApacheTest):
z_domains = []
for achall in self.achalls:
self.sni.add_chall(achall)
z_domain = achall.gen_response(self.auth_key).z_domain
z_domain = achall.response(self.auth_key).z_domain
z_domains.append(set([z_domain]))
self.sni._mod_config() # pylint: disable=protected-access

View file

@ -60,7 +60,7 @@ def test_authenticator(plugin, config, temp_dir):
"Plugin failed to complete %s for %s in %s",
type(achalls[i]), achalls[i].domain, config)
success = False
elif isinstance(responses[i], challenges.DVSNIResponse):
elif isinstance(responses[i], challenges.TLSSNI01):
verify = functools.partial(responses[i].simple_verify, achalls[i],
achalls[i].domain,
util.JWK.public_key(),
@ -68,10 +68,10 @@ def test_authenticator(plugin, config, temp_dir):
port=plugin.https_port)
if _try_until_true(verify):
logger.info(
"DVSNI verification for %s succeeded", achalls[i].domain)
"tls-sni-01 verification for %s succeeded", achalls[i].domain)
else:
logger.error(
"DVSNI verification for %s in %s failed",
"tls-sni-01 verification for %s in %s failed",
achalls[i].domain, config)
success = False
@ -99,12 +99,12 @@ def _create_achalls(plugin):
for domain in names:
prefs = plugin.get_chall_pref(domain)
for chall_type in prefs:
if chall_type == challenges.DVSNI:
chall = challenges.DVSNI(
token=os.urandom(challenges.DVSNI.TOKEN_SIZE))
if chall_type == challenges.TLSSNI01:
chall = challenges.TLSSNI01(
token=os.urandom(challenges.TLSSNI01.TOKEN_SIZE))
challb = acme_util.chall_to_challb(
chall, messages.STATUS_PENDING)
achall = achallenges.DVSNI(
achall = achallenges.KeyAuthorizationAnnotatedChallenge(
challb=challb, domain=domain, account_key=util.JWK)
achalls.append(achall)

View file

@ -14,7 +14,6 @@ import zope.interface
from acme import challenges
from acme import crypto_util as acme_crypto_util
from letsencrypt import achallenges
from letsencrypt import constants as core_constants
from letsencrypt import crypto_util
from letsencrypt import errors
@ -297,7 +296,7 @@ class NginxConfigurator(common.Plugin):
"""Make a server SSL.
Make a server SSL based on server_name and filename by adding a
``listen IConfig.dvsni_port ssl`` directive to the server block.
``listen IConfig.tls_sni_01_port ssl`` directive to the server block.
.. todo:: Maybe this should create a new block instead of modifying
the existing one?
@ -307,7 +306,7 @@ class NginxConfigurator(common.Plugin):
"""
snakeoil_cert, snakeoil_key = self._get_snakeoil_paths()
ssl_block = [['listen', '{0} ssl'.format(self.config.dvsni_port)],
ssl_block = [['listen', '{0} ssl'.format(self.config.tls_sni_01_port)],
# access and error logs necessary for integration
# testing (non-root)
['access_log', os.path.join(
@ -321,7 +320,8 @@ class NginxConfigurator(common.Plugin):
vhost.filep, vhost.names, ssl_block)
vhost.ssl = True
vhost.raw.extend(ssl_block)
vhost.addrs.add(obj.Addr('', str(self.config.dvsni_port), True, False))
vhost.addrs.add(obj.Addr(
'', str(self.config.tls_sni_01_port), True, False))
def get_all_certs_keys(self):
"""Find all existing keys, certs from configuration.
@ -536,7 +536,7 @@ class NginxConfigurator(common.Plugin):
###########################################################################
def get_chall_pref(self, unused_domain): # pylint: disable=no-self-use
"""Return list of challenge preferences."""
return [challenges.DVSNI]
return [challenges.TLSSNI01]
# Entry point in main.py for performing challenges
def perform(self, achalls):
@ -552,11 +552,10 @@ class NginxConfigurator(common.Plugin):
nginx_dvsni = dvsni.NginxDvsni(self)
for i, achall in enumerate(achalls):
if isinstance(achall, achallenges.DVSNI):
# Currently also have dvsni hold associated index
# of the challenge. This helps to put all of the responses back
# together when they are all complete.
nginx_dvsni.add_chall(achall, i)
# Currently also have dvsni hold associated index
# of the challenge. This helps to put all of the responses back
# together when they are all complete.
nginx_dvsni.add_chall(achall, i)
sni_response = nginx_dvsni.perform()
# Must restart in order to activate the challenges.

View file

@ -13,7 +13,7 @@ from letsencrypt_nginx import nginxparser
logger = logging.getLogger(__name__)
class NginxDvsni(common.Dvsni):
class NginxDvsni(common.TLSSNI01):
"""Class performs DVSNI challenges within the Nginx configurator.
:ivar configurator: NginxConfigurator object
@ -48,7 +48,7 @@ class NginxDvsni(common.Dvsni):
addresses = []
default_addr = "{0} default_server ssl".format(
self.configurator.config.dvsni_port)
self.configurator.config.tls_sni_01_port)
for achall in self.achalls:
vhost = self.configurator.choose_vhost(achall.domain)
@ -141,7 +141,7 @@ class NginxDvsni(common.Dvsni):
block = [['listen', str(addr)] for addr in addrs]
block.extend([['server_name',
achall.gen_response(achall.account_key).z_domain],
achall.response(achall.account_key).z_domain],
['include', self.configurator.parser.loc["ssl_options"]],
# access and error logs necessary for
# integration testing (non-root)

View file

@ -51,7 +51,7 @@ class NginxConfiguratorTest(util.NginxTest):
errors.PluginError, self.config.enhance, 'myhost', 'redirect')
def test_get_chall_pref(self):
self.assertEqual([challenges.DVSNI],
self.assertEqual([challenges.TLSSNI01],
self.config.get_chall_pref('myhost'))
def test_save(self):
@ -210,22 +210,22 @@ class NginxConfiguratorTest(util.NginxTest):
def test_perform(self, mock_restart, mock_dvsni_perform):
# Only tests functionality specific to configurator.perform
# Note: As more challenges are offered this will have to be expanded
achall1 = achallenges.DVSNI(
achall1 = achallenges.KeyAuthorizationAnnotatedChallenge(
challb=messages.ChallengeBody(
chall=challenges.DVSNI(token="kNdwjwOeX0I_A8DXt9Msmg"),
chall=challenges.TLSSNI01(token="kNdwjwOeX0I_A8DXt9Msmg"),
uri="https://ca.org/chall0_uri",
status=messages.Status("pending"),
), domain="localhost", account_key=self.rsa512jwk)
achall2 = achallenges.DVSNI(
achall2 = achallenges.KeyAuthorizationAnnotatedChallenge(
challb=messages.ChallengeBody(
chall=challenges.DVSNI(token="m8TdO1qik4JVFtgPPurJmg"),
chall=challenges.TLSSNI01(token="m8TdO1qik4JVFtgPPurJmg"),
uri="https://ca.org/chall1_uri",
status=messages.Status("pending"),
), domain="example.com", account_key=self.rsa512jwk)
dvsni_ret_val = [
achall1.gen_response(self.rsa512jwk),
achall2.gen_response(self.rsa512jwk),
achall1.response(self.rsa512jwk),
achall2.response(self.rsa512jwk),
]
mock_dvsni_perform.return_value = dvsni_ret_val

View file

@ -19,22 +19,22 @@ from letsencrypt_nginx.tests import util
class DvsniPerformTest(util.NginxTest):
"""Test the NginxDVSNI challenge."""
account_key = common_test.DvsniTest.auth_key
account_key = common_test.TLSSNI01Test.auth_key
achalls = [
achallenges.DVSNI(
achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.chall_to_challb(
challenges.DVSNI(token="kNdwjwOeX0I_A8DXt9Msmg"), "pending"),
challenges.TLSSNI01(token="kNdwjwOeX0I_A8DXt9Msmg"), "pending"),
domain="www.example.com", account_key=account_key),
achallenges.DVSNI(
achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.chall_to_challb(
challenges.DVSNI(
challenges.TLSSNI01(
token="\xba\xa9\xda?<m\xaewmx\xea\xad\xadv\xf4\x02\xc9y"
"\x80\xe2_X\t\xe7\xc7\xa4\t\xca\xf7&\x945"
), "pending"),
domain="blah", account_key=account_key),
achallenges.DVSNI(
achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.chall_to_challb(
challenges.DVSNI(
challenges.TLSSNI01(
token="\x8c\x8a\xbf_-f\\cw\xee\xd6\xf8/\xa5\xe3\xfd"
"\xeb9\xf1\xf5\xb9\xefVM\xc9w\xa4u\x9c\xe1\x87\xb4"
), "pending"),
@ -70,7 +70,7 @@ class DvsniPerformTest(util.NginxTest):
@mock.patch("letsencrypt_nginx.configurator.NginxConfigurator.save")
def test_perform1(self, mock_save):
self.sni.add_chall(self.achalls[0])
response = self.achalls[0].gen_response(self.account_key)
response = self.achalls[0].response(self.account_key)
mock_setup_cert = mock.MagicMock(return_value=response)
# pylint: disable=protected-access
@ -92,7 +92,7 @@ class DvsniPerformTest(util.NginxTest):
acme_responses = []
for achall in self.achalls:
self.sni.add_chall(achall)
acme_responses.append(achall.gen_response(self.account_key))
acme_responses.append(achall.response(self.account_key))
mock_setup_cert = mock.MagicMock(side_effect=acme_responses)
# pylint: disable=protected-access
@ -139,9 +139,9 @@ class DvsniPerformTest(util.NginxTest):
for vhost in vhs:
if vhost.addrs == set(v_addr1):
response = self.achalls[0].gen_response(self.account_key)
response = self.achalls[0].response(self.account_key)
else:
response = self.achalls[2].gen_response(self.account_key)
response = self.achalls[2].response(self.account_key)
self.assertEqual(vhost.addrs, set(v_addr2))
self.assertEqual(vhost.names, set([response.z_domain]))

View file

@ -59,7 +59,7 @@ def get_nginx_configurator(
temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"),
in_progress_dir=os.path.join(backups, "IN_PROGRESS"),
server="https://acme-server.org:443/new",
dvsni_port=5001,
tls_sni_01_port=5001,
),
name="nginx",
version=version)

View file

@ -49,40 +49,10 @@ class KeyAuthorizationAnnotatedChallenge(AnnotatedChallenge):
"""Client annotated `KeyAuthorizationChallenge` challenge."""
__slots__ = ('challb', 'domain', 'account_key')
def response_and_validation(self):
def response_and_validation(self, *args, **kwargs):
"""Generate response and validation."""
return self.challb.chall.response_and_validation(self.account_key)
class DVSNI(AnnotatedChallenge):
"""Client annotated "dvsni" ACME challenge.
:ivar .JWK account_key: Authorized Account Key
"""
__slots__ = ('challb', 'domain', 'account_key')
acme_type = challenges.DVSNI
def gen_cert_and_response(self, key=None, bits=2048, alg=jose.RS256):
"""Generate a DVSNI cert and response.
:param OpenSSL.crypto.PKey key: Private key used for
certificate generation. If none provided, a fresh key will
be generated.
:param int bits: Number of bits for fresh key generation.
:param .JWAAlgorithm alg:
:returns: ``(response, cert_pem, key_pem)`` tuple, where
``response`` is an instance of
`acme.challenges.DVSNIResponse`, ``cert`` is a certificate
(`OpenSSL.crypto.X509`) and ``key`` is a private key
(`OpenSSL.crypto.PKey`).
:rtype: tuple
"""
response = self.challb.chall.gen_response(self.account_key, alg=alg)
cert, key = response.gen_cert(key=key, bits=bits)
return response, cert, key
return self.challb.chall.response_and_validation(
self.account_key, *args, **kwargs)
class DNS(AnnotatedChallenge):

View file

@ -344,8 +344,8 @@ def challb_to_achall(challb, account_key, domain):
chall = challb.chall
logger.info("%s challenge for %s", chall.typ, domain)
if isinstance(chall, challenges.DVSNI):
return achallenges.DVSNI(
if isinstance(chall, challenges.KeyAuthorizationChallenge):
return achallenges.KeyAuthorizationAnnotatedChallenge(
challb=challb, domain=domain, account_key=account_key)
elif isinstance(chall, challenges.DNS):
return achallenges.DNS(challb=challb, domain=domain)
@ -355,9 +355,6 @@ def challb_to_achall(challb, account_key, domain):
elif isinstance(chall, challenges.ProofOfPossession):
return achallenges.ProofOfPossession(
challb=challb, domain=domain)
elif isinstance(chall, challenges.KeyAuthorizationChallenge):
return achallenges.KeyAuthorizationAnnotatedChallenge(
challb=challb, domain=domain, account_key=account_key)
else:
raise errors.Error(
"Received unsupported challenge of type: %s", chall.typ)

View file

@ -850,8 +850,9 @@ def prepare_and_parse_args(plugins, args):
help=config_help("no_verify_ssl"),
default=flag_default("no_verify_ssl"))
helpful.add(
"testing", "--dvsni-port", type=int, default=flag_default("dvsni_port"),
help=config_help("dvsni_port"))
"testing", "--tls-sni-01-port", type=int,
default=flag_default("tls_sni_01_port"),
help=config_help("tls_sni_01_port"))
helpful.add("testing", "--http-01-port", dest="http01_port", type=int,
help=config_help("http01_port"))

View file

@ -37,10 +37,10 @@ class NamespaceConfig(object):
def __init__(self, namespace):
self.namespace = namespace
if self.http01_port == self.dvsni_port:
if self.http01_port == self.tls_sni_01_port:
raise errors.Error(
"Trying to run http-01 and DVSNI "
"on the same port ({0})".format(self.dvsni_port))
"Trying to run http-01 and tls-sni-01 "
"on the same port ({0})".format(self.tls_sni_01_port))
def __getattr__(self, name):
return getattr(self.namespace, name)

View file

@ -23,7 +23,7 @@ CLI_DEFAULTS = dict(
work_dir="/var/lib/letsencrypt",
logs_dir="/var/log/letsencrypt",
no_verify_ssl=False,
dvsni_port=challenges.DVSNI.PORT,
tls_sni_01_port=challenges.TLSSNI01Response.PORT,
auth_cert_path="./cert.pem",
auth_chain_path="./chain.pem",
@ -41,7 +41,7 @@ RENEWER_DEFAULTS = dict(
EXCLUSIVE_CHALLENGES = frozenset([frozenset([
challenges.DVSNI, challenges.HTTP01])])
challenges.TLSSNI01, challenges.HTTP01])])
"""Mutually exclusive challenges."""

View file

@ -57,8 +57,8 @@ class DvAuthError(AuthorizationError):
# Authenticator - Challenge specific errors
class DvsniError(DvAuthError):
"""Let's Encrypt DVSNI error."""
class TLSSNI01Error(DvAuthError):
"""Let's Encrypt TLSSNI01 error."""
# Plugin Errors

View file

@ -219,8 +219,8 @@ class IConfig(zope.interface.Interface):
no_verify_ssl = zope.interface.Attribute(
"Disable SSL certificate verification.")
dvsni_port = zope.interface.Attribute(
"Port number to perform DVSNI challenge. "
tls_sni_01_port = zope.interface.Attribute(
"Port number to perform tls-sni-01 challenge. "
"Boulder in testing mode defaults to 5001.")
http01_port = zope.interface.Attribute(

View file

@ -135,22 +135,22 @@ class Addr(object):
return self.__class__((self.tup[0], port))
class Dvsni(object):
"""Class that perform DVSNI challenges."""
class TLSSNI01(object):
"""Class that performs tls-sni-01 challenges."""
def __init__(self, configurator):
self.configurator = configurator
self.achalls = []
self.indices = []
self.challenge_conf = os.path.join(
configurator.config.config_dir, "le_dvsni_cert_challenge.conf")
configurator.config.config_dir, "le_tls_sni_01_cert_challenge.conf")
# self.completed = 0
def add_chall(self, achall, idx=None):
"""Add challenge to DVSNI object to perform at once.
"""Add challenge to TLSSNI01 object to perform at once.
:param achall: Annotated DVSNI challenge.
:type achall: :class:`letsencrypt.achallenges.DVSNI`
:param .KeyAuthorizationAnnotatedChallenge achall: Annotated
TLSSNI01 challenge.
:param int idx: index to challenge in a larger array
@ -162,8 +162,8 @@ class Dvsni(object):
def get_cert_path(self, achall):
"""Returns standardized name for challenge certificate.
:param achall: Annotated DVSNI challenge.
:type achall: :class:`letsencrypt.achallenges.DVSNI`
:param .KeyAuthorizationAnnotatedChallenge achall: Annotated
tls-sni-01 challenge.
:returns: certificate file name
:rtype: str
@ -177,7 +177,7 @@ class Dvsni(object):
return os.path.join(self.configurator.config.work_dir,
achall.chall.encode("token") + '.pem')
def _setup_challenge_cert(self, achall, s=None):
def _setup_challenge_cert(self, achall, cert_key=None):
"""Generate and write out challenge certificate."""
cert_path = self.get_cert_path(achall)
@ -186,7 +186,8 @@ class Dvsni(object):
self.configurator.reverter.register_file_creation(True, key_path)
self.configurator.reverter.register_file_creation(True, cert_path)
response, cert, key = achall.gen_cert_and_response(s)
response, (cert, key) = achall.response_and_validation(
cert_key=cert_key)
cert_pem = OpenSSL.crypto.dump_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert)
key_pem = OpenSSL.crypto.dump_privatekey(

View file

@ -115,24 +115,24 @@ class AddrTest(unittest.TestCase):
self.assertEqual(set_a, set_b)
class DvsniTest(unittest.TestCase):
"""Tests for letsencrypt.plugins.common.DvsniTest."""
class TLSSNI01Test(unittest.TestCase):
"""Tests for letsencrypt.plugins.common.TLSSNI01."""
auth_key = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
achalls = [
achallenges.DVSNI(
achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.chall_to_challb(
challenges.DVSNI(token=b'dvsni1'), "pending"),
challenges.TLSSNI01(token=b'token1'), "pending"),
domain="encryption-example.demo", account_key=auth_key),
achallenges.DVSNI(
achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.chall_to_challb(
challenges.DVSNI(token=b'dvsni2'), "pending"),
challenges.TLSSNI01(token=b'token2'), "pending"),
domain="letsencrypt.demo", account_key=auth_key),
]
def setUp(self):
from letsencrypt.plugins.common import Dvsni
self.sni = Dvsni(configurator=mock.MagicMock())
from letsencrypt.plugins.common import TLSSNI01
self.sni = TLSSNI01(configurator=mock.MagicMock())
def test_add_chall(self):
self.sni.add_chall(self.achalls[0], 0)
@ -146,11 +146,11 @@ class DvsniTest(unittest.TestCase):
# http://www.voidspace.org.uk/python/mock/helpers.html#mock.mock_open
mock_open, mock_safe_open = mock.mock_open(), mock.mock_open()
response = challenges.DVSNIResponse(validation=mock.Mock())
response = challenges.TLSSNI01Response()
achall = mock.MagicMock()
key = test_util.load_pyopenssl_private_key("rsa512_key.pem")
achall.gen_cert_and_response.return_value = (
response, test_util.load_cert("cert.pem"), key)
achall.response_and_validation.return_value = (
response, (test_util.load_cert("cert.pem"), key))
with mock.patch("letsencrypt.plugins.common.open",
mock_open, create=True):

View file

@ -31,7 +31,7 @@ class Authenticator(common.Plugin):
run as a privileged process. Alternatively shows instructions on how
to use Python's built-in HTTP server.
.. todo:: Support for `~.challenges.DVSNI`.
.. todo:: Support for `~.challenges.TLSSNI01`.
"""
zope.interface.implements(interfaces.IAuthenticator)

View file

@ -11,7 +11,6 @@ import six
import zope.interface
from acme import challenges
from acme import crypto_util as acme_crypto_util
from acme import standalone as acme_standalone
from letsencrypt import errors
@ -28,9 +27,9 @@ class ServerManager(object):
Manager for `ACMEServer` and `ACMETLSServer` instances.
`certs` and `simple_http_resources` correspond to
`certs` and `http_01_resources` correspond to
`acme.crypto_util.SSLSocket.certs` and
`acme.crypto_util.SSLSocket.simple_http_resources` respectively. All
`acme.crypto_util.SSLSocket.http_01_resources` respectively. All
created servers share the same certificates and resources, so if
you're running both TLS and non-TLS instances, HTTP01 handlers
will serve the same URLs!
@ -38,10 +37,10 @@ class ServerManager(object):
"""
_Instance = collections.namedtuple("_Instance", "server thread")
def __init__(self, certs, simple_http_resources):
def __init__(self, certs, http_01_resources):
self._instances = {}
self.certs = certs
self.simple_http_resources = simple_http_resources
self.http_01_resources = http_01_resources
def run(self, port, challenge_type):
"""Run ACME server on specified ``port``.
@ -51,23 +50,23 @@ class ServerManager(object):
:param int port: Port to run the server on.
:param challenge_type: Subclass of `acme.challenges.Challenge`,
either `acme.challenge.HTTP01` or `acme.challenges.DVSNI`.
either `acme.challenge.HTTP01` or `acme.challenges.TLSSNI01`.
:returns: Server instance.
:rtype: ACMEServerMixin
"""
assert challenge_type in (challenges.DVSNI, challenges.HTTP01)
assert challenge_type in (challenges.TLSSNI01, challenges.HTTP01)
if port in self._instances:
return self._instances[port].server
address = ("", port)
try:
if challenge_type is challenges.DVSNI:
server = acme_standalone.DVSNIServer(address, self.certs)
if challenge_type is challenges.TLSSNI01:
server = acme_standalone.TLSSNI01Server(address, self.certs)
else: # challenges.HTTP01
server = acme_standalone.HTTP01Server(
address, self.simple_http_resources)
address, self.http_01_resources)
except socket.error as error:
raise errors.StandaloneBindError(error, port)
@ -109,7 +108,7 @@ class ServerManager(object):
in six.iteritems(self._instances))
SUPPORTED_CHALLENGES = set([challenges.DVSNI, challenges.HTTP01])
SUPPORTED_CHALLENGES = set([challenges.TLSSNI01, challenges.HTTP01])
def supported_challenges_validator(data):
@ -138,7 +137,7 @@ class Authenticator(common.Plugin):
"""Standalone Authenticator.
This authenticator creates its own ephemeral TCP listener on the
necessary port in order to respond to incoming DVSNI and HTTP01
necessary port in order to respond to incoming tls-sni-01 and http-01
challenges from the certificate authority. Therefore, it does not
rely on any existing server program.
"""
@ -150,12 +149,9 @@ class Authenticator(common.Plugin):
def __init__(self, *args, **kwargs):
super(Authenticator, self).__init__(*args, **kwargs)
# one self-signed key for all DVSNI and HTTP01 certificates
# one self-signed key for all tls-sni-01 certificates
self.key = OpenSSL.crypto.PKey()
self.key.generate_key(OpenSSL.crypto.TYPE_RSA, bits=2048)
# TODO: generate only when the first HTTP01 challenge is solved
self.simple_http_cert = acme_crypto_util.gen_ss_cert(
self.key, domains=["temp server"])
self.served = collections.defaultdict(set)
@ -164,9 +160,9 @@ class Authenticator(common.Plugin):
# GIL, the operations are safe, c.f.
# https://docs.python.org/2/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe
self.certs = {}
self.simple_http_resources = set()
self.http_01_resources = set()
self.servers = ServerManager(self.certs, self.simple_http_resources)
self.servers = ServerManager(self.certs, self.http_01_resources)
@classmethod
def add_parser_arguments(cls, add):
@ -186,15 +182,16 @@ class Authenticator(common.Plugin):
necessary_ports = set()
if challenges.HTTP01 in self.supported_challenges:
necessary_ports.add(self.config.http01_port)
if challenges.DVSNI in self.supported_challenges:
necessary_ports.add(self.config.dvsni_port)
if challenges.TLSSNI01 in self.supported_challenges:
necessary_ports.add(self.config.tls_sni_01_port)
return necessary_ports
def more_info(self): # pylint: disable=missing-docstring
return("This authenticator creates its own ephemeral TCP listener "
"on the necessary port in order to respond to incoming DVSNI "
"and HTTP01 challenges from the certificate authority. "
"Therefore, it does not rely on any existing server program.")
"on the necessary port in order to respond to incoming "
"tls-sni-01 and http-01 challenges from the certificate "
"authority. Therefore, it does not rely on any existing "
"server program.")
def prepare(self): # pylint: disable=missing-docstring
pass
@ -240,17 +237,16 @@ class Authenticator(common.Plugin):
server = self.servers.run(
self.config.http01_port, challenges.HTTP01)
response, validation = achall.response_and_validation()
self.simple_http_resources.add(
self.http_01_resources.add(
acme_standalone.HTTP01RequestHandler.HTTP01Resource(
chall=achall.chall, response=response,
validation=validation))
cert = self.simple_http_cert
domain = achall.domain
else: # DVSNI
server = self.servers.run(self.config.dvsni_port, challenges.DVSNI)
response, cert, _ = achall.gen_cert_and_response(self.key)
domain = response.z_domain
self.certs[domain] = (self.key, cert)
else: # tls-sni-01
server = self.servers.run(
self.config.tls_sni_01_port, challenges.TLSSNI01)
response, (cert, _) = achall.response_and_validation(
cert_key=self.key)
self.certs[response.z_domain] = (self.key, cert)
self.served[server].add(achall)
responses.append(response)

View file

@ -24,13 +24,13 @@ class ServerManagerTest(unittest.TestCase):
def setUp(self):
from letsencrypt.plugins.standalone import ServerManager
self.certs = {}
self.simple_http_resources = {}
self.mgr = ServerManager(self.certs, self.simple_http_resources)
self.http_01_resources = {}
self.mgr = ServerManager(self.certs, self.http_01_resources)
def test_init(self):
self.assertTrue(self.mgr.certs is self.certs)
self.assertTrue(
self.mgr.simple_http_resources is self.simple_http_resources)
self.mgr.http_01_resources is self.http_01_resources)
def _test_run_stop(self, challenge_type):
server = self.mgr.run(port=0, challenge_type=challenge_type)
@ -39,10 +39,10 @@ class ServerManagerTest(unittest.TestCase):
self.mgr.stop(port=port)
self.assertEqual(self.mgr.running(), {})
def test_run_stop_dvsni(self):
self._test_run_stop(challenges.DVSNI)
def test_run_stop_tls_sni_01(self):
self._test_run_stop(challenges.TLSSNI01)
def test_run_stop_simplehttp(self):
def test_run_stop_http_01(self):
self._test_run_stop(challenges.HTTP01)
def test_run_idempotent(self):
@ -73,10 +73,10 @@ class SupportedChallengesValidatorTest(unittest.TestCase):
return supported_challenges_validator(data)
def test_correct(self):
self.assertEqual("dvsni", self._call("dvsni"))
self.assertEqual("tls-sni-01", self._call("tls-sni-01"))
self.assertEqual("http-01", self._call("http-01"))
self.assertEqual("dvsni,http-01", self._call("dvsni,http-01"))
self.assertEqual("http-01,dvsni", self._call("http-01,dvsni"))
self.assertEqual("tls-sni-01,http-01", self._call("tls-sni-01,http-01"))
self.assertEqual("http-01,tls-sni-01", self._call("http-01,tls-sni-01"))
def test_unrecognized(self):
assert "foo" not in challenges.Challenge.TYPES
@ -92,24 +92,24 @@ class AuthenticatorTest(unittest.TestCase):
def setUp(self):
from letsencrypt.plugins.standalone import Authenticator
self.config = mock.MagicMock(
dvsni_port=1234, http01_port=4321,
standalone_supported_challenges="dvsni,http-01")
tls_sni_01_port=1234, http01_port=4321,
standalone_supported_challenges="tls-sni-01,http-01")
self.auth = Authenticator(self.config, name="standalone")
def test_supported_challenges(self):
self.assertEqual(self.auth.supported_challenges,
set([challenges.DVSNI, challenges.HTTP01]))
set([challenges.TLSSNI01, challenges.HTTP01]))
def test_more_info(self):
self.assertTrue(isinstance(self.auth.more_info(), six.string_types))
def test_get_chall_pref(self):
self.assertEqual(set(self.auth.get_chall_pref(domain=None)),
set([challenges.DVSNI, challenges.HTTP01]))
set([challenges.TLSSNI01, challenges.HTTP01]))
@mock.patch("letsencrypt.plugins.standalone.util")
def test_perform_alredy_listening(self, mock_util):
for chall, port in ((challenges.DVSNI.typ, 1234),
for chall, port in ((challenges.TLSSNI01.typ, 1234),
(challenges.HTTP01.typ, 4321)):
mock_util.already_listening.return_value = True
self.config.standalone_supported_challenges = chall
@ -153,10 +153,10 @@ class AuthenticatorTest(unittest.TestCase):
def test_perform2(self):
domain = b'localhost'
key = jose.JWK.load(test_util.load_vector('rsa512_key.pem'))
simple_http = achallenges.KeyAuthorizationAnnotatedChallenge(
http_01 = achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.HTTP01_P, domain=domain, account_key=key)
dvsni = achallenges.DVSNI(
challb=acme_util.DVSNI_P, domain=domain, account_key=key)
tls_sni_01 = achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.TLSSNI01_P, domain=domain, account_key=key)
self.auth.servers = mock.MagicMock()
@ -164,24 +164,24 @@ class AuthenticatorTest(unittest.TestCase):
return "server{0}".format(port)
self.auth.servers.run.side_effect = _run
responses = self.auth.perform2([simple_http, dvsni])
responses = self.auth.perform2([http_01, tls_sni_01])
self.assertTrue(isinstance(responses, list))
self.assertEqual(2, len(responses))
self.assertTrue(isinstance(responses[0], challenges.HTTP01Response))
self.assertTrue(isinstance(responses[1], challenges.DVSNIResponse))
self.assertTrue(isinstance(responses[1], challenges.TLSSNI01Response))
self.assertEqual(self.auth.servers.run.mock_calls, [
mock.call(4321, challenges.HTTP01),
mock.call(1234, challenges.DVSNI),
mock.call(1234, challenges.TLSSNI01),
])
self.assertEqual(self.auth.served, {
"server1234": set([dvsni]),
"server4321": set([simple_http]),
"server1234": set([tls_sni_01]),
"server4321": set([http_01]),
})
self.assertEqual(1, len(self.auth.simple_http_resources))
self.assertEqual(2, len(self.auth.certs))
self.assertEqual(list(self.auth.simple_http_resources), [
self.assertEqual(1, len(self.auth.http_01_resources))
self.assertEqual(1, len(self.auth.certs))
self.assertEqual(list(self.auth.http_01_resources), [
acme_standalone.HTTP01RequestHandler.HTTP01Resource(
acme_util.HTTP01, responses[0], mock.ANY)])

View file

@ -75,7 +75,7 @@ def renew(cert, old_version):
# XXX: this loses type data (for example, the fact that key_size
# was an int, not a str)
config.rsa_key_size = int(config.rsa_key_size)
config.dvsni_port = int(config.dvsni_port)
config.tls_sni_01_port = int(config.tls_sni_01_port)
config.namespace.http01_port = int(config.namespace.http01_port)
zope.component.provideUtility(config)
try:

View file

@ -1,34 +0,0 @@
"""Tests for letsencrypt.achallenges."""
import unittest
import OpenSSL
from acme import challenges
from acme import jose
from letsencrypt.tests import acme_util
from letsencrypt.tests import test_util
class DVSNITest(unittest.TestCase):
"""Tests for letsencrypt.achallenges.DVSNI."""
def setUp(self):
self.challb = acme_util.chall_to_challb(acme_util.DVSNI, "pending")
key = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
from letsencrypt.achallenges import DVSNI
self.achall = DVSNI(
challb=self.challb, domain="example.com", account_key=key)
def test_proxy(self):
self.assertEqual(self.challb.token, self.achall.token)
def test_gen_cert_and_response(self):
response, cert, key = self.achall.gen_cert_and_response()
self.assertTrue(isinstance(response, challenges.DVSNIResponse))
self.assertTrue(isinstance(cert, OpenSSL.crypto.X509))
self.assertTrue(isinstance(key, OpenSSL.crypto.PKey))
if __name__ == "__main__":
unittest.main() # pragma: no cover

View file

@ -14,7 +14,7 @@ KEY = test_util.load_rsa_private_key('rsa512_key.pem')
# Challenges
HTTP01 = challenges.HTTP01(
token="evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA")
DVSNI = challenges.DVSNI(
TLSSNI01 = challenges.TLSSNI01(
token=jose.b64decode(b"evaGxfADs6pSRb2LAv9IZf17Dt3juxGJyPCt92wrDoA"))
DNS = challenges.DNS(token="17817c66b60ce2e4012dfad92657527a")
RECOVERY_CONTACT = challenges.RecoveryContact(
@ -41,7 +41,7 @@ POP = challenges.ProofOfPossession(
)
)
CHALLENGES = [HTTP01, DVSNI, DNS, RECOVERY_CONTACT, POP]
CHALLENGES = [HTTP01, TLSSNI01, DNS, RECOVERY_CONTACT, POP]
DV_CHALLENGES = [chall for chall in CHALLENGES
if isinstance(chall, challenges.DVChallenge)]
CONT_CHALLENGES = [chall for chall in CHALLENGES
@ -79,13 +79,13 @@ def chall_to_challb(chall, status): # pylint: disable=redefined-outer-name
# Pending ChallengeBody objects
DVSNI_P = chall_to_challb(DVSNI, messages.STATUS_PENDING)
TLSSNI01_P = chall_to_challb(TLSSNI01, messages.STATUS_PENDING)
HTTP01_P = chall_to_challb(HTTP01, messages.STATUS_PENDING)
DNS_P = chall_to_challb(DNS, messages.STATUS_PENDING)
RECOVERY_CONTACT_P = chall_to_challb(RECOVERY_CONTACT, messages.STATUS_PENDING)
POP_P = chall_to_challb(POP, messages.STATUS_PENDING)
CHALLENGES_P = [HTTP01_P, DVSNI_P, DNS_P, RECOVERY_CONTACT_P, POP_P]
CHALLENGES_P = [HTTP01_P, TLSSNI01_P, DNS_P, RECOVERY_CONTACT_P, POP_P]
DV_CHALLENGES_P = [challb for challb in CHALLENGES_P
if isinstance(challb.chall, challenges.DVChallenge)]
CONT_CHALLENGES_P = [

View file

@ -45,7 +45,7 @@ class ChallengeFactoryTest(unittest.TestCase):
self.assertEqual(
[achall.chall for achall in cont_c], [acme_util.RECOVERY_CONTACT])
self.assertEqual([achall.chall for achall in dv_c], [acme_util.DVSNI])
self.assertEqual([achall.chall for achall in dv_c], [acme_util.TLSSNI01])
def test_unrecognized(self):
self.handler.authzr["failure.com"] = acme_util.gen_authzr(
@ -70,7 +70,7 @@ class GetAuthorizationsTest(unittest.TestCase):
self.mock_dv_auth = mock.MagicMock(name="ApacheConfigurator")
self.mock_cont_auth = mock.MagicMock(name="ContinuityAuthenticator")
self.mock_dv_auth.get_chall_pref.return_value = [challenges.DVSNI]
self.mock_dv_auth.get_chall_pref.return_value = [challenges.TLSSNI01]
self.mock_cont_auth.get_chall_pref.return_value = [
challenges.RecoveryContact]
@ -90,7 +90,7 @@ class GetAuthorizationsTest(unittest.TestCase):
logging.disable(logging.NOTSET)
@mock.patch("letsencrypt.auth_handler.AuthHandler._poll_challenges")
def test_name1_dvsni1(self, mock_poll):
def test_name1_tls_sni_01_1(self, mock_poll):
self.mock_net.request_domain_challenges.side_effect = functools.partial(
gen_dom_authzr, challs=acme_util.DV_CHALLENGES)
@ -107,14 +107,14 @@ class GetAuthorizationsTest(unittest.TestCase):
self.assertEqual(self.mock_dv_auth.cleanup.call_count, 1)
self.assertEqual(self.mock_cont_auth.cleanup.call_count, 0)
# Test if list first element is DVSNI, use typ because it is an achall
# Test if list first element is TLSSNI01, use typ because it is an achall
self.assertEqual(
self.mock_dv_auth.cleanup.call_args[0][0][0].typ, "dvsni")
self.mock_dv_auth.cleanup.call_args[0][0][0].typ, "tls-sni-01")
self.assertEqual(len(authzr), 1)
@mock.patch("letsencrypt.auth_handler.AuthHandler._poll_challenges")
def test_name3_dvsni3_rectok_3(self, mock_poll):
def test_name3_tls_sni_01_3_rectok_3(self, mock_poll):
self.mock_net.request_domain_challenges.side_effect = functools.partial(
gen_dom_authzr, challs=acme_util.CHALLENGES)
@ -309,9 +309,9 @@ class GenChallengePathTest(unittest.TestCase):
return gen_challenge_path(challbs, preferences, combinations)
def test_common_case(self):
"""Given DVSNI and HTTP01 with appropriate combos."""
challbs = (acme_util.DVSNI_P, acme_util.HTTP01_P)
prefs = [challenges.DVSNI]
"""Given TLSSNI01 and HTTP01 with appropriate combos."""
challbs = (acme_util.TLSSNI01_P, acme_util.HTTP01_P)
prefs = [challenges.TLSSNI01]
combos = ((0,), (1,))
# Smart then trivial dumb path test
@ -324,9 +324,9 @@ class GenChallengePathTest(unittest.TestCase):
def test_common_case_with_continuity(self):
challbs = (acme_util.POP_P,
acme_util.RECOVERY_CONTACT_P,
acme_util.DVSNI_P,
acme_util.TLSSNI01_P,
acme_util.HTTP01_P)
prefs = [challenges.ProofOfPossession, challenges.DVSNI]
prefs = [challenges.ProofOfPossession, challenges.TLSSNI01]
combos = acme_util.gen_combos(challbs)
self.assertEqual(self._call(challbs, prefs, combos), (0, 2))
@ -336,14 +336,14 @@ class GenChallengePathTest(unittest.TestCase):
def test_full_cont_server(self):
challbs = (acme_util.RECOVERY_CONTACT_P,
acme_util.POP_P,
acme_util.DVSNI_P,
acme_util.TLSSNI01_P,
acme_util.HTTP01_P,
acme_util.DNS_P)
# Typical webserver client that can do everything except DNS
# Attempted to make the order realistic
prefs = [challenges.ProofOfPossession,
challenges.HTTP01,
challenges.DVSNI,
challenges.TLSSNI01,
challenges.RecoveryContact]
combos = acme_util.gen_combos(challbs)
self.assertEqual(self._call(challbs, prefs, combos), (1, 3))
@ -352,8 +352,8 @@ class GenChallengePathTest(unittest.TestCase):
self.assertTrue(self._call(challbs, prefs, None))
def test_not_supported(self):
challbs = (acme_util.POP_P, acme_util.DVSNI_P)
prefs = [challenges.DVSNI]
challbs = (acme_util.POP_P, acme_util.TLSSNI01_P)
prefs = [challenges.TLSSNI01]
combos = ((0, 1),)
self.assertRaises(
@ -411,7 +411,7 @@ class IsPreferredTest(unittest.TestCase):
def _call(cls, chall, satisfied):
from letsencrypt.auth_handler import is_preferred
return is_preferred(chall, satisfied, exclusive_groups=frozenset([
frozenset([challenges.DVSNI, challenges.HTTP01]),
frozenset([challenges.TLSSNI01, challenges.HTTP01]),
frozenset([challenges.DNS, challenges.HTTP01]),
]))
@ -421,11 +421,11 @@ class IsPreferredTest(unittest.TestCase):
def test_mutually_exclusvie(self):
self.assertFalse(
self._call(
acme_util.DVSNI_P, frozenset([acme_util.HTTP01_P])))
acme_util.TLSSNI01_P, frozenset([acme_util.HTTP01_P])))
def test_mutually_exclusive_same_type(self):
self.assertTrue(
self._call(acme_util.DVSNI_P, frozenset([acme_util.DVSNI_P])))
self._call(acme_util.TLSSNI01_P, frozenset([acme_util.TLSSNI01_P])))
class ReportFailedChallsTest(unittest.TestCase):
@ -446,15 +446,15 @@ class ReportFailedChallsTest(unittest.TestCase):
domain="example.com",
account_key="key")
kwargs["chall"] = acme_util.DVSNI
self.dvsni_same = achallenges.DVSNI(
kwargs["chall"] = acme_util.TLSSNI01
self.tls_sni_same = achallenges.KeyAuthorizationAnnotatedChallenge(
# pylint: disable=star-args
challb=messages.ChallengeBody(**kwargs),
domain="example.com",
account_key="key")
kwargs["error"] = messages.Error(typ="dnssec", detail="detail")
self.dvsni_diff = achallenges.DVSNI(
self.tls_sni_diff = achallenges.KeyAuthorizationAnnotatedChallenge(
# pylint: disable=star-args
challb=messages.ChallengeBody(**kwargs),
domain="foo.bar",
@ -464,7 +464,7 @@ class ReportFailedChallsTest(unittest.TestCase):
def test_same_error_and_domain(self, mock_zope):
from letsencrypt import auth_handler
auth_handler._report_failed_challs([self.http01, self.dvsni_same])
auth_handler._report_failed_challs([self.http01, self.tls_sni_same])
call_list = mock_zope().add_message.call_args_list
self.assertTrue(len(call_list) == 1)
self.assertTrue("Domains: example.com\n" in call_list[0][0][0])
@ -473,7 +473,7 @@ class ReportFailedChallsTest(unittest.TestCase):
def test_different_errors_and_domains(self, mock_zope):
from letsencrypt import auth_handler
auth_handler._report_failed_challs([self.http01, self.dvsni_diff])
auth_handler._report_failed_challs([self.http01, self.tls_sni_diff])
self.assertTrue(mock_zope().add_message.call_count == 2)

View file

@ -14,12 +14,12 @@ class NamespaceConfigTest(unittest.TestCase):
self.namespace = mock.MagicMock(
config_dir='/tmp/config', work_dir='/tmp/foo', foo='bar',
server='https://acme-server.org:443/new',
dvsni_port=1234, http01_port=4321)
tls_sni_01_port=1234, http01_port=4321)
from letsencrypt.configuration import NamespaceConfig
self.config = NamespaceConfig(self.namespace)
def test_init_same_ports(self):
self.namespace.dvsni_port = 4321
self.namespace.tls_sni_01_port = 4321
from letsencrypt.configuration import NamespaceConfig
self.assertRaises(errors.Error, NamespaceConfig, self.namespace)

View file

@ -34,7 +34,7 @@ class PerformTest(unittest.TestCase):
def test_unexpected(self):
self.assertRaises(
errors.ContAuthError, self.auth.perform, [
achallenges.DVSNI(
achallenges.KeyAuthorizationAnnotatedChallenge(
challb=None, domain="0", account_key="invalid_key")])
def test_chall_pref(self):
@ -53,7 +53,7 @@ class CleanupTest(unittest.TestCase):
mock.MagicMock(server="demo_server.org"), None)
def test_unexpected(self):
unexpected = achallenges.DVSNI(
unexpected = achallenges.KeyAuthorizationAnnotatedChallenge(
challb=None, domain="0", account_key="dummy_key")
self.assertRaises(errors.ContAuthError, self.auth.cleanup, [unexpected])

View file

@ -688,7 +688,7 @@ class RenewableCertTests(BaseRenewableCertTest):
self.test_rc.configfile["renewalparams"]["rsa_key_size"] = "2048"
self.test_rc.configfile["renewalparams"]["server"] = "acme.example.com"
self.test_rc.configfile["renewalparams"]["authenticator"] = "fake"
self.test_rc.configfile["renewalparams"]["dvsni_port"] = "4430"
self.test_rc.configfile["renewalparams"]["tls_sni_01_port"] = "4430"
self.test_rc.configfile["renewalparams"]["http01_port"] = "1234"
self.test_rc.configfile["renewalparams"]["account"] = "abcde"
mock_auth = mock.MagicMock()

View file

@ -27,7 +27,7 @@ common() {
"$@"
}
common --domains le1.wtf --standalone-supported-challenges dvsni auth
common --domains le1.wtf --standalone-supported-challenges tls-sni-01 auth
common --domains le2.wtf --standalone-supported-challenges http-01 run
common -a manual -d le.wtf auth

View file

@ -15,7 +15,7 @@ letsencrypt_test () {
letsencrypt \
--server "${SERVER:-http://localhost:4000/directory}" \
--no-verify-ssl \
--dvsni-port 5001 \
--tls-sni-01-port 5001 \
--http-01-port 5002 \
--manual-test-mode \
$store_flags \