From 9e5c19bc51163c1b34f07678d5f13ba7baed940f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20K=C3=A4stel?= Date: Mon, 21 May 2018 21:46:25 +0200 Subject: [PATCH] Rough draft of External Account Binding. --- acme/acme/messages.py | 30 ++++++++++++++++++++++++++++++ certbot/cli.py | 17 +++++++++++++++++ certbot/client.py | 5 ++++- certbot/log.py | 1 + 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 03dbc3255..334ff4288 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -1,6 +1,7 @@ """ACME protocol messages.""" import collections import six +import json import josepy as jose @@ -274,6 +275,7 @@ class Registration(ResourceBody): agreement = jose.Field('agreement', omitempty=True) status = jose.Field('status', omitempty=True) terms_of_service_agreed = jose.Field('termsOfServiceAgreed', omitempty=True) + external_account_binding = jose.Field('externalAccountBinding', omitempty=True) phone_prefix = 'tel:' email_prefix = 'mailto:' @@ -289,6 +291,34 @@ class Registration(ResourceBody): kwargs['contact'] = tuple(details) return cls(**kwargs) + @classmethod + def from_eab_data(cls, key=None, kid=None, hmac=None, phone=None, email=None, **kwargs): + """Create registration resource from contact details for EAB use.""" + details = list(kwargs.pop('contact', ())) + if phone is not None: + details.append(cls.phone_prefix + phone) + if email is not None: + details.append(cls.email_prefix + email) + kwargs['contact'] = tuple(details) + + # jwk_key = jose.jwk.JWKRSA(key=key) + key_json = json.dumps(key.public_key().to_partial_json()) + # b64_key_json = jose.b64.b64encode(key_json) + hmac_key = jose.b64.b64decode(hmac) + jws = jose.JWS.sign( + payload=key_json, + key=jose.jwk.JWKOct(key=hmac_key), + alg=jose.jwa.HS256, + kid=kid, + include_jwk=False, + protect=frozenset(['alg', 'kid']) + ) + # eab_content = json.dumps(jws.to_partial_json()) + eab_content = jws.to_partial_json() + + kwargs['external_account_binding'] = eab_content + return cls(**kwargs) + def _filter_contact(self, prefix): return tuple( detail[len(prefix):] for detail in self.contact diff --git a/certbot/cli.py b/certbot/cli.py index 8a1ad381a..0d0eea519 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -935,6 +935,23 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis "specified or you already have a certificate with the same " "name. In the case of a name collision it will append a number " "like 0001 to the file path name. (default: Ask)") + helpful.add( + [None, "run", "certonly"], + "--eab", action="store_true", dest="eab", + help="Use External Account Binding for account registration." + ) + helpful.add( + [None, "run", "certonly"], + "--kid", dest="kid", + metavar="KID", + help="Key Identifier for External Account Binding" + ) + helpful.add( + [None, "run", "certonly"], + "--hmac", dest="hmac", + metavar="HMAC", + help="HMAC for External Account Binding" + ) helpful.add( [None, "run", "certonly", "manage", "delete", "certificates", "renew", "enhance"], "--cert-name", dest="certname", diff --git a/certbot/client.py b/certbot/client.py index 1932ab83e..253c00621 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -192,7 +192,10 @@ def perform_registration(acme, config, tos_cb): :rtype: `acme.messages.RegistrationResource` """ try: - return acme.new_account_and_tos(messages.NewRegistration.from_data(email=config.email), + return acme.new_account_and_tos(messages.NewRegistration.from_eab_data(key=acme.client.net.key, + kid=config.kid, + hmac=config.hmac, + email=config.email), tos_cb) except messages.Error as e: if e.code == "invalidEmail" or e.code == "invalidContact": diff --git a/certbot/log.py b/certbot/log.py index 89626af99..608524f88 100644 --- a/certbot/log.py +++ b/certbot/log.py @@ -313,6 +313,7 @@ def post_arg_parse_except_hook(exc_type, exc_value, trace, debug, log_path): :param str log_path: path to file or directory containing the log """ + debug = True exc_info = (exc_type, exc_value, trace) # constants.QUIET_LOGGING_LEVEL or higher should be used to # display message the user, otherwise, a lower level like