Chiffrement de bout en bout (E2EE)
MorisBox chiffre le contenu in-app afin que les serveurs ne stockent que le ciphertext et les métadonnées. Les clés privées ne quittent jamais les appareils utilisateur ou agent.
Ce qui est chiffré
| Contenu | E2EE |
|---|---|
| Conversations support (utilisateur ↔ agents partenaire) | Oui |
Messages hub marqués encryption: "e2e" | Oui |
| Médias dans les enveloppes E2E | Oui |
| Corps OTP / SMS / repli WhatsApp | Non (doivent être lisibles sur ces canaux) |
| Métadonnées (partenaire, horodatages, état, taille) | Non |
Les messages E2E stockent le contenu uniquement in-app. Si l'utilisateur n'est pas en ligne dans l'app (aucune activité appareil depuis ~5 minutes), MorisBox envoie aussi une alerte opaque WhatsApp (puis SMS) :
Nouveau message de {partner} dans votre boîte MorisBox. Ouvrez l’app pour le lire (message sécurisé).
Cette alerte n'inclut jamais le corps chiffré. Les codes OTP et de connexion ne sont pas E2EE afin que les utilisateurs puissent toujours accéder à leur compte.
Protocole (v1)
| Élément | Algorithme |
|---|---|
| Identité appareil | X25519 |
| Accord de clé | ECDH (X25519) |
| KDF | HKDF-SHA-256 |
| Chiffrement du contenu | AES-256-GCM |
| Encodage filaire | Base64url |
content_key → AES-GCM encrypt(plaintext JSON)
wrap(content_key) per recipient device via ECDH + AES-GCM
Le double ratchet de style Signal n'est pas en v1 (durcissement prévu).
Annuaire de clés appareil
Appareils utilisateur
Après connexion, le client génère une paire de clés d'identité et publie la moitié publique :
POST /api/v1/me/devices/keys
Authorization: Bearer <user_jwt>
Content-Type: application/json
{
"identity_key_pub": "<b64url x25519 public key>"
}
Appareils agent partenaire (Partner Hub)
POST /api/v1/agent/devices/keys
Authorization: Bearer <partner_api_key>
{
"device_uuid": "<browser session uuid>",
"identity_key_pub": "<b64url>"
}
Récupérer les clés pour un téléphone
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": [ ... ]
}
Envoyer un message hub chiffré
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"
}
Le serveur stocke uniquement l'enveloppe. body n'est jamais persisté en clair.
Erreurs :
| Code | Signification |
|---|---|
e2e_no_devices | L'utilisateur n'a pas encore publié de clés d'identité |
validation | e2e_envelope.ciphertext / nonce manquant |
Messages de conversation (client)
POST /api/v1/me/conversations/<ref>/messages
Authorization: Bearer <user_jwt>
{
"encrypted": true,
"e2e_mode": "conversation",
"e2e_key_version": 3,
"e2e_envelope": { ... }
}
Uploader les wraps de content-key :
POST /api/v1/me/conversations/<ref>/keys
{
"key_version": 3,
"wraps": [
{
"device_uuid": "...",
"device_kind": "user",
"wrap": "...",
"wrap_nonce": "...",
"sender_eph_pub": "..."
}
]
}
Annuaire des pairs :
GET /api/v1/me/conversations/<ref>/devices
Comportement client
| Client | Comportement |
|---|---|
| Application Client / Mobile | Générer les clés à la connexion ; chiffrer le chat ; déchiffrer les enveloppes pour l'affichage |
| Partner Hub | Publier les clés agent ; chiffrer les réponses / envois hub scellés |
| Serveur | Route le ciphertext ; ne déchiffre jamais |
L'UI affiche un libellé cadenas sur les messages chiffrés. Si cet appareil n'a pas de wrap, l'utilisateur voit « impossible de déchiffrer ».
Notes de sécurité
- Les clés privées restent sur l'appareil (SecureStore / stockage local ; durcir vers des clés matérielles lorsque disponible).
- L'AAD lie l'enveloppe à une chaîne de contexte message pour réduire les attaques par substitution.
- Les appareils révoqués sont exclus des nouveaux wraps.
- Ne journalisez pas les corps déchiffrés sur les serveurs ou passerelles.
Voir aussi
- Plateformes - où s'applique l'E2EE
- Messages - types de contenu
- Conversations - fils support
