Criptografia de ponta a ponta (E2EE)

O MorisBox criptografa o conteúdo in-app para que os servidores armazenem somente o ciphertext e os metadados. As chaves privadas nunca saem dos dispositivos do usuário ou do agente.

O que é criptografado

ConteúdoE2EE
Conversas de suporte (usuário ↔ agentes do parceiro)Sim
Mensagens do hub marcadas com encryption: "e2e"Sim
Mídia em envelopes E2ESim
Corpo de OTP / SMS / fallback por WhatsAppNão (precisa ser legível nesses canais)
Metadados (parceiro, timestamps, status, tamanho)Não

As mensagens E2E armazenam o conteúdo somente in-app. Se o usuário não estiver online no aplicativo (nenhuma atividade do dispositivo por cerca de 5 minutos), o MorisBox também envia um alerta opaco pelo WhatsApp (e depois por SMS):

Nova mensagem de {partner} na sua caixa MorisBox. Abra o aplicativo para lê-la (mensagem segura).

Esse alerta nunca inclui o corpo criptografado. Os códigos OTP e de login não usam E2EE, para que os usuários sempre possam acessar a conta.

Protocolo (v1)

ElementoAlgoritmo
Identidade do dispositivoX25519
Acordo de chavesECDH (X25519)
KDFHKDF-SHA-256
Criptografia do conteúdoAES-256-GCM
Codificação de transporteBase64url
content_key  →  AES-GCM encrypt(plaintext JSON)
wrap(content_key) per recipient device via ECDH + AES-GCM

O double ratchet no estilo Signal não faz parte da v1 (o reforço está previsto).

Diretório de chaves dos dispositivos

Dispositivos do usuário

Após o login, o cliente gera um par de chaves de identidade e publica a metade pública:

POST /api/v1/me/devices/keys
Authorization: Bearer <user_jwt>
Content-Type: application/json

{
  "identity_key_pub": "<b64url x25519 public key>"
}

Dispositivos de agentes do parceiro (Partner Hub)

POST /api/v1/agent/devices/keys
Authorization: Bearer <partner_api_key>

{
  "device_uuid": "<browser session uuid>",
  "identity_key_pub": "<b64url>"
}

Obter as chaves de um telefone

GET /api/v1/keys/+228XXXXXXXX
Authorization: Bearer <partner_api_key>
{
  "phone": "+228XXXXXXXX",
  "devices": [
    {
      "device_uuid": "...",
      "identity_key_pub": "...",
      "platform": "web",
      "has_keys": true
    }
  ],
  "agent_devices": [ ... ]
}

Enviar uma mensagem criptografada ao hub

POST /api/v1/messages
Authorization: Bearer <partner_api_key>
Content-Type: application/json

{
  "phone": "+228XXXXXXXX",
  "encryption": "e2e",
  "e2e_envelope": {
    "v": 1,
    "alg": "x25519-hkdf-sha256-aes-256-gcm",
    "mode": "sealed",
    "key_version": 0,
    "sender_device_uuid": "partner-agent-…",
    "ciphertext": "<b64url>",
    "nonce": "<b64url>",
    "aad": "seed360|msg|<ref>|v1",
    "key_wraps": [
      {
        "device_uuid": "user-device-…",
        "wrap": "<b64url>",
        "wrap_nonce": "<b64url>",
        "sender_eph_pub": "<b64url>"
      }
    ]
  },
  "content_type": "text"
}

O servidor armazena somente o envelope. body nunca é persistido em texto simples.

Erros:

CódigoSignificado
e2e_no_devicesO usuário ainda não publicou chaves de identidade
validatione2e_envelope.ciphertext / nonce ausente

Mensagens de conversa (cliente)

POST /api/v1/me/conversations/<ref>/messages
Authorization: Bearer <user_jwt>

{
  "encrypted": true,
  "e2e_mode": "conversation",
  "e2e_key_version": 3,
  "e2e_envelope": { ... }
}

Envie os wraps da chave de conteúdo:

POST /api/v1/me/conversations/<ref>/keys
{
  "key_version": 3,
  "wraps": [
    {
      "device_uuid": "...",
      "device_kind": "user",
      "wrap": "...",
      "wrap_nonce": "...",
      "sender_eph_pub": "..."
    }
  ]
}

Diretório de participantes:

GET /api/v1/me/conversations/<ref>/devices

Comportamento do cliente

ClienteComportamento
Application Client / móvelGerar as chaves no login; criptografar o chat; descriptografar os envelopes para exibição
Partner HubPublicar as chaves do agente; criptografar as respostas / os envios selados ao hub
ServidorRotear o ciphertext; nunca descriptografar

A interface exibe um indicador de cadeado nas mensagens criptografadas. Se esse dispositivo não tiver um wrap, o usuário verá “não foi possível descriptografar”.

Observações de segurança

  • As chaves privadas permanecem no dispositivo (SecureStore / armazenamento local; evolua para chaves respaldadas por hardware quando disponíveis).
  • O AAD vincula o envelope a uma string de contexto da mensagem para reduzir ataques de substituição.
  • Os dispositivos revogados são excluídos dos novos wraps.
  • Não registre corpos descriptografados em servidores ou gateways.

Veja também