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údo | E2EE |
|---|---|
| Conversas de suporte (usuário ↔ agentes do parceiro) | Sim |
Mensagens do hub marcadas com encryption: "e2e" | Sim |
| Mídia em envelopes E2E | Sim |
| Corpo de OTP / SMS / fallback por WhatsApp | Nã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)
| Elemento | Algoritmo |
|---|---|
| Identidade do dispositivo | X25519 |
| Acordo de chaves | ECDH (X25519) |
| KDF | HKDF-SHA-256 |
| Criptografia do conteúdo | AES-256-GCM |
| Codificação de transporte | Base64url |
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ódigo | Significado |
|---|---|
e2e_no_devices | O usuário ainda não publicou chaves de identidade |
validation | e2e_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
| Cliente | Comportamento |
|---|---|
| Application Client / móvel | Gerar as chaves no login; criptografar o chat; descriptografar os envelopes para exibição |
| Partner Hub | Publicar as chaves do agente; criptografar as respostas / os envios selados ao hub |
| Servidor | Rotear 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
- Plataformas: onde o E2EE se aplica
- Mensagens: tipos de conteúdo
- Conversas: conversas de suporte