class Revocation(josepy.JSONObjectWithFields): """Message type for certificate revocation requests.""" certificate: "cryptography.x509.Certificate" = josepy.Field( "certificate", decoder=decode_cert, encoder=encode_cert) """The certificate to be revoked.""" reason: RevocationReason = josepy.Field( "reason", decoder=RevocationReason, encoder=lambda reason: reason.value, omitempty=True, ) """The reason for the revocation."""
class Meta(jose.JSONObjectWithFields): """Account metadata :ivar datetime.datetime creation_dt: Creation date and time (UTC). :ivar str creation_host: FQDN of host, where account has been created. :ivar str register_to_eff: If not None, Certbot will register the provided email during the account registration. .. note:: ``creation_dt`` and ``creation_host`` are useful in cross-machine migration scenarios. """ creation_dt = acme_fields.RFC3339Field("creation_dt") creation_host = jose.Field("creation_host") register_to_eff = jose.Field("register_to_eff", omitempty=True)
class EmailReply00(KeyAuthorizationChallenge): response_cls = EmailReply00Response typ = response_cls.typ from_addr = jose.Field("from") def validation(self, account_key, **unused_kwargs): return self.key_authorization(account_key)
class Account(josepy.JSONObjectWithFields): """Patched :class:`acme.messages.Registration` message type that adds a *kid* field. This is the representation of a user account that the :class:`~acmetk.client.AcmeClient` uses internally. The :attr:`kid` field is sent to the remote server with every request and used for request verification. Fields that see no use inside the client have been removed. """ status: AccountStatus = josepy.Field("status", omitempty=True) """The account's status.""" contact: typing.Tuple[str] = josepy.Field("contact", omitempty=True) """The account's contact info.""" orders: str = josepy.Field("orders", omitempty=True) """URL of the account's orders list.""" kid: str = josepy.Field("kid") """The account's key ID."""
class Header(jose.Header): """ACME-specific JOSE Header. Implements nonce, kid, and url. """ nonce = jose.Field('nonce', omitempty=True, encoder=jose.encode_b64jose) kid = jose.Field('kid', omitempty=True) url = jose.Field('url', omitempty=True) # Mypy does not understand the josepy magic happening here, and falsely claims # that nonce is redefined. Let's ignore the type check here. @nonce.decoder # type: ignore def nonce(value): # pylint: disable=no-self-argument,missing-function-docstring try: return jose.decode_b64jose(value) except jose.DeserializationError as error: # TODO: custom error raise jose.DeserializationError("Invalid nonce: {0}".format(error))
class KeyAuthorizationChallengeResponse(ChallengeResponse): """Response to Challenges based on Key Authorization. :param unicode key_authorization: """ key_authorization = jose.Field("keyAuthorization") thumbprint_hash_function = hashes.SHA256 def __init__(self, *args, **kwargs): super(KeyAuthorizationChallengeResponse, self).__init__(*args, **kwargs) self._dump_authorization_key(False) def verify(self, chall, account_public_key): """Verify the key authorization. :param KeyAuthorization chall: Challenge that corresponds to this response. :param JWK account_public_key: :return: ``True`` iff verification of the key authorization was successful. :rtype: bool """ parts = self.key_authorization.split('.') # pylint: disable=no-member if len(parts) != 2: logger.debug("Key authorization (%r) is not well formed", self.key_authorization) return False if parts[0] != chall.encode("token"): logger.debug("Mismatching token in key authorization: " "%r instead of %r", parts[0], chall.encode("token")) return False thumbprint = jose.b64encode(account_public_key.thumbprint( hash_function=self.thumbprint_hash_function)).decode() if parts[1] != thumbprint: logger.debug("Mismatching thumbprint in key authorization: " "%r instead of %r", parts[0], thumbprint) return False return True def _dump_authorization_key(self, dump): # type: (bool) -> None """ Set if keyAuthorization is dumped in the JSON representation of this ChallengeResponse. NB: This method is declared as private because it will eventually be removed. :param bool dump: True to dump the keyAuthorization, False otherwise """ object.__setattr__(self, '_dump_auth_key', dump) def to_partial_json(self): jobj = super(KeyAuthorizationChallengeResponse, self).to_partial_json() if not self._dump_auth_key: # pylint: disable=no-member jobj.pop('keyAuthorization', None) return jobj
class Registration(ResourceBody): """Registration Resource Body. :ivar josepy.jwk.JWK key: Public key. :ivar tuple contact: Contact information following ACME spec, `tuple` of `unicode`. :ivar unicode agreement: """ # on new-reg key server ignores 'key' and populates it based on # JWS.signature.combined.jwk key = jose.Field('key', omitempty=True, decoder=jose.JWK.from_json) contact = jose.Field('contact', omitempty=True, default=()) agreement = jose.Field('agreement', omitempty=True) status = jose.Field('status', omitempty=True) terms_of_service_agreed = jose.Field('termsOfServiceAgreed', omitempty=True) phone_prefix = 'tel:' email_prefix = 'mailto:' @classmethod def from_data(cls, phone=None, email=None, **kwargs): """Create registration resource from contact details.""" 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) return cls(**kwargs) def _filter_contact(self, prefix): return tuple(detail[len(prefix):] for detail in self.contact if detail.startswith(prefix)) @property def phones(self): """All phones found in the ``contact`` field.""" return self._filter_contact(self.phone_prefix) @property def emails(self): """All emails found in the ``contact`` field.""" return self._filter_contact(self.email_prefix)
class SignedKeyChange(josepy.JSONObjectWithFields): protected = josepy.Field("protected") payload = josepy.Field("payload") signature = josepy.Field("signature") @classmethod def from_data(cls, kc, key, alg, **kwargs): data = acme.jws.JWS.sign(kc.json_dumps().encode(), key=key, alg=alg, nonce=None, **kwargs) signature = josepy.b64.b64encode(data.signature.signature).decode() payload = josepy.b64.b64encode(data.payload).decode() protected = josepy.b64.b64encode( data.signature.protected.encode()).decode() return cls(protected=protected, payload=payload, signature=signature)
class CertificateRequest(josepy.JSONObjectWithFields): """Message type for certificate requests. Received by an :class:`~acmetk.server.AcmeServerBase` instance at the order finalization step :meth:`~acmetk.server.AcmeServerBase.finalize_order`. """ csr: "cryptography.x509.CertificateSigningRequest" = josepy.Field( "csr", decoder=decode_csr, encoder=encode_csr) """The certificate signing request."""
class AccountUpdate(JSONDeSerializableAllowEmpty, acme.messages.Registration): """Patched :class:`acme.messages.Registration` message type that adds a *status* field for status update requests. Inherits from :class:`JSONDeSerializableAllowEmpty` so that POST-as-GET requests don't result in a parsing error. """ status: AccountStatus = josepy.Field("status", decoder=AccountStatus, omitempty=True) """The account's new status."""
class Signature(jose.Signature): """ACME-specific Signature. Uses ACME-specific Header for customer fields.""" __slots__ = jose.Signature._orig_slots # pylint: disable=no-member # TODO: decoder/encoder should accept cls? Otherwise, subclassing # JSONObjectWithFields is tricky... header_cls = Header header = jose.Field('header', omitempty=True, default=header_cls(), decoder=header_cls.from_json)
class Order(acme.messages.Order): """Patched :class:`acme.messages.Order` message type that adds a *URL* field. The *URL* field is populated by copying the *Location* header from responses in the :class:`~acmetk.client.AcmeClient`. This field is used by the :class:`~acmetk.server.AcmeProxy` to store the *proxied* URL and to be able to map an internal order to that of the remote CA. """ url: str = josepy.Field("url", omitempty=True) """The order's URL at the remote CA."""