Example #1
0
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."""
Example #2
0
    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)
Example #3
0
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)
Example #4
0
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."""
Example #5
0
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))
Example #6
0
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
Example #7
0
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)
Example #8
0
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)
Example #9
0
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."""
Example #10
0
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."""
Example #11
0
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)
Example #12
0
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."""