Зашифровать payload#

Алгоритм принадлежит приложению.

Eazy SDK задаёт порядок преобразований, но не поставляет cipher, keyring или KMS-клиент. Алгоритм, ключи, nonce и их ротация остаются в приложении.

# examples/mail/http/encryption.py
MAIL_CRYPTO = payload_crypto(
    "mail-send-v1",
    outbound=encrypt_outbound(
        encrypt_field(NewMessage, lambda message: message.subject, using=FIELD_CIPHER),
        encrypt_field(NewMessage, lambda message: message.body, using=FIELD_CIPHER),
        encoded=encrypt_encoded(
            using=BODY_CIPHER,
            max_input_bytes=16_000,
            max_output_bytes=24_000,
        ),
    ),
    inbound=decrypt_inbound(
        encoded=decrypt_encoded(
            using=BODY_CIPHER,
            max_input_bytes=24_000,
            max_output_bytes=16_000,
        )
    ),
)

MAIL_WIRE = http_encrypted(
    content_type=MAIL_ENCRYPTED_CONTENT_TYPE,
    clear_content_type="application/json",
    plaintext_statuses=frozenset({401, 403}),
)

Профиль сначала шифрует выбранные поля модели, затем JSON-кодек создаёт байты и encrypt_encoded(...) шифрует тело целиком. Входящий ответ проходит обратные стадии.

# examples/mail/http/encryption.py
@dataclass(frozen=True, slots=True, kw_only=True)
class SendEncryptedMessage(HttpOperation[SendResult]):
    __http__ = Http.post(
        "/api/v1/messages/send",
        success={200: Json(SendEnvelope)},
        errors={200: (Json(RejectedEnvelope), RecipientRejected)},
        security=MAIL_BEARER,
        signing=MAIL_HMAC,
        crypto=MAIL_CRYPTO,
        wire=Wire(encrypted=MAIL_WIRE),
    )

    body: Annotated[NewMessage, JsonBody()]


class EncryptedMailApi(SyncApi):
    base_url = "https://mail.example"

    send = op(SendEncryptedMessage)
    sent = op(FindSent)

http_encrypted(...) объявляет внешний и открытый Content-Type. Статусы из plaintext_statuses разрешено разбирать без расшифровки; остальные несовпадения закрывают вызов ошибкой.

# examples/docs/encryption.py
def main() -> None:
    message_id, clear_subject = send_encrypted_message()
    print(f"message: {message_id}")
    print(f"decrypted subject: {clear_subject}")
Запускаемый файл целиком
"""Run the documented field and whole-body encryption profile."""

from examples.mail.http.encryption import send_encrypted_message


# region docs: encryption-run
# examples/docs/encryption.py
def main() -> None:
    message_id, clear_subject = send_encrypted_message()
    print(f"message: {message_id}")
    print(f"decrypted subject: {clear_subject}")
# endregion docs: encryption-run


if __name__ == "__main__":
    main()
message: 100
decrypted subject: Секретная встреча

Порядок стадий#

исходящий: модель → поля → кодек → байты → подпись → отправка
входящий: ответ → байты → кодек → поля → проверка модели

Подпись видит ciphertext, если операция использует и шифрование, и signing. Каждая повторная попытка заново выполняет преобразования, поэтому nonce и ciphertext предыдущей попытки не переиспользуются.

Для WebSocket укажите websocket_encrypted(...). Шифрование всего frame обычно требует binary frame; text разрешён только для алгоритма, который гарантирует валидный UTF-8, тогда как ping, pong и close остаются управляющими frames и вообще не проходят payload crypto.

Typed metadata для key id или nonce объявляется через CryptoResult и связывается с конкретным wire-slot. Необъявленное, пропущенное или конфликтующее значение приводит к закрытой ошибке.

Дальше#