Documento técnico de referência

Atestado de Conformidade E2EE
Versão técnica detalhada

Documento completo que descreve a arquitetura criptográfica da leggit, o modelo de ameaças, as ocorrências da auditoria security-engineer e o respetivo tratamento, bem como a metodologia de reverificação.

Referência: ATT-E2EE-2026-05-18-v2.0
Data de emissão: 2026-05-18
Âmbito do software: app.leggit.org (cofre), leggit.org (site público)
Edição: v2.0 — verificação de não regressão v1.0 + auditoria de alterações posteriores
Histórico: v1.0 arquivada (16/05/2026) · v2.0 fonte markdown

📋 Atualização v2.0 (2026-05-18)

Esta edição combina duas passagens de auditoria security-engineer:

  • v1.0 (16/05) — 12 achados: 1 HIGH (enumeração de telefones), 4 MEDIUM (rate-limit, CRLF filename, cleanup pairings, quota), 4 LOW (Cache-Control, audit id_coffre, reveal Apenas tabela, Referrer-Policy), 3 INFO. Todos corrigidos e verificados sem regressão em v2.0.
  • v2.0 (18/05) — 5 novos achados: 1 HIGH (reatribuição destrutiva window.LeggitCoffreFile quebrando o upload E2EE dual-device), 2 MEDIUM (reutilizações de placeholder PDO :u causando SQLSTATE[HY093] em cofres familiares e validate paranoia), 2 LOW (rate-limit ausente em get_my_privkey/request_recovery, comparação não em tempo constante do código de autorização). Todos corrigidos.

Alterações de arquitetura E2EE desde v1.0

  • Tabela de substituição (modo Prudente) — Anteriormente carregada via endpoint legado upload_file (cifragem do lado do servidor, etiqueta «servidor»). Agora via fluxo chunked E2EE completo: upload_init_e2ee + upload_chunk_e2ee (cabeçalho secretstream + chunks de 1 MiB) + upload_finalize_e2ee. O servidor nunca vê a foto em claro, mesmo temporariamente. Etiqueta «E2EE».
  • Workflow dual-device (PC ↔ telemóvel) — Permite introduzir as palavras substituídas no PC e fotografar a tabela no telemóvel, sem que um único dispositivo tenha simultaneamente os dois segredos. Pairing: token longo 256 bits (armazenado em SHA-256), código curto 6 caracteres (alfabeto sem confundíveis), one-shot atómico via UPDATE … WHERE consume_le IS NULL, TTL 24h, rate-limit baseado em ficheiros.
  • Revelação Prudente em 3 modos — Durante a consulta, o utilizador escolhe antes da decifragem: «Apenas palavras», «Apenas tabela» (a seed NÃO é decifrada), «Ambos». O ramo «Apenas tabela» ignora a chamada a LeggitCoffreCrypto.decryptItem do lado do navegador, coerente com a promessa dual-device.
  • Tabela de referência relations_types — Tabela BD administrável (apenas super-admin) que retira os tipos de relação (Cônjuge, Filho, Pai…) do código. Cada tipo carrega a sua relação espelho (filho↔pai, neto↔avô, etc.) e uma flag «herdeiro por defeito». Prepara a fase 2 (tabela de ligação user_relations entre 2 utilizadores com fluxo de validação).
  • Testes de integridade — Passaram de 753 a 774+ asserções (secções P11.52 a P11.63 adicionadas para bloquear qualquer regressão futura sobre os temas auditados).

Nenhuma falha criptográfica. As primitivas permanecem inalteradas (XChaCha20-Poly1305 / X25519 / Argon2id / Shamir GF(2⁸)). Os achados são bypasses de rate-limit, vetores de enumeração a nível aplicacional e — para o HIGH v2.0 — um bug funcional JS (sem fuga de dados).

1.Preâmbulo e âmbito

O presente atestado documenta o estado de conformidade do sistema leggit perante as normas de cifragem ponto-a-ponto (E2EE) baseadas no navegador e as ameaças de segurança aplicacional conhecidas, à data de emissão.

Abrange

  • O módulo de cofres pessoais (texto, ficheiros, vídeos até 100 MB)
  • O módulo de cofres familiares (texto + ficheiros multi-membros)
  • O módulo de crypto-wallets (modos Standard / Prudente / Paranoia)
  • O pipeline de autenticação e de recuperação post-mortem
  • A infraestrutura de segurança (CSP, SRI, HSTS, registos de auditoria)

Não abrange

  • A segurança do equipamento do utilizador (navegador comprometido, extensão maliciosa, malware local — fora do âmbito técnico da leggit)
  • As comunicações fora da leggit (email externo, SMS recebidos através de um operador)
  • Os serviços terceiros externos (Stripe para os pagamentos, OVH para o alojamento — cuja segurança é objeto dos respetivos atestados)

2.Arquitetura E2EE — síntese técnica

2.1 Princípio de não-acesso pelo servidor

O servidor leggit nunca possui:

  • Os conteúdos em claro (textos, ficheiros, vídeos, seeds BIP-39)
  • A chave KEK_user derivada da palavra-passe do utilizador
  • As chaves DEK (Data Encryption Key) geradas por item

O servidor apenas armazena e manipula:

  • Cipher blobs cifrados com AEAD XChaCha20-Poly1305-IETF
  • Wraps DEK cifrados (AEAD para o proprietário, crypto_box_seal X25519 para os destinatários / membros da família)
  • Fingerprints DEK (SHA-256, verificação de coerência sem revelação da DEK)

2.2 Primitivas criptográficas

PrimitivaAlgoritmoUtilização
AEAD simétricoXChaCha20-Poly1305-IETFCifra dos itens + chunks de ficheiros
Streaming chunkedsecretstream_xchacha20poly1305Ficheiros > 4 MiB
KDF palavra-passeArgon2id (crypto_pwhash)KEK do utilizador
AssimétricoX25519 / crypto_box_sealWraps de destinatários + família
Partilha de segredosShamir GF(2⁸)Modo PARANOIA crypto-wallets (M-of-N)
HashSHA-256 (WebCrypto)dek_fingerprint
Integridade de scriptsSRI sha384Todos os JS críticos

2.3 Isolamento Web Worker

Todas as operações criptográficas são executadas num Web Worker isolado (assets/js/coffre-crypto-worker.js). A main thread:

  • Nunca tem acesso à KEK_user (transferida via ArrayBuffer Transferable — ver MED-3)
  • Nunca tem acesso à privkey X25519 decifrada
  • Nunca tem acesso às DEK em claro
  • Nunca tem acesso às palavras BIP-39 ou às shares Shamir antes do box_seal

Consequência: um XSS na main thread (vetor residual) não é suficiente para extrair as chaves.

2.4 Armazenamento no servidor

Todos os dados sensíveis na base de dados são opacos:

SELECT cipher_blob FROM coffre_items WHERE crypto_version LIKE 'e2ee-%';
-- → BLOB binário, nenhuma correlação possível com um plaintext conhecido

Um dump completo da base de dados não permite a recuperação dos conteúdos sem a KEK_user de cada utilizador (que apenas existe na memória do navegador).

3.Modelo de ameaças

3.1 Ameaças cobertas (protegidas)

VetorProteção
Dump frio da base de dados (roubo de disco, snapshot, cópia de segurança)Cifra inutilizável sem a KEK do utilizador
Leitor insider passivo (administrador curioso, registos, consultas SQL)Dados opacos mesmo para o DBA
RCE de servidor "instantâneo" que não modifica o código-fonteCifra permanece opaca, KEK nunca transmitida em claro
Rede intermédia (proxy, ISP, NSA passivo)TLS + HSTS + cifra aplicacional redundante
Roubo de cookie de sessão + replayReautenticação reauth_until_e2ee exigida para as operações sensíveis (CR-S3, HIGH-1)
Scraping massivo dos registos de auditoria por sessão comprometidaRate-limit 30/60s + deteção de scrape (MED-2)
Forja de wraps com recipients fora da whitelistVerificação de whitelist + fingerprint coerente (CR-S2)

3.2 Ameaças NÃO cobertas (limites assumidos)

O sistema não protege contra:

  • RCE de servidor ativo que modifica o JavaScript servido. Mitigado por SRI sha384 + sub-resource versioning + CSP, mas não eliminado. Um atacante com acesso de administrador ao servidor web pode substituir o código de criptografia.
  • Navegador do utilizador comprometido (malware, extensão maliciosa, keylogger). Fora do âmbito técnico.
  • Constrangimento judicial que force a modificação do servidor (lawfare). A leggit está sediada em França, sujeita aos pedidos judiciais aplicáveis.
  • Criptanálise futura de XChaCha20 ou X25519. Risco residual a 10-20 anos, vigiado pela comunidade NIST/IETF.

Estes limites são comunicados explicitamente ao utilizador final na página pública /securite e na documentação de marketing.

4.Metodologia de auditoria

4.1 Âmbito verificado

A auditoria incidiu sobre:

  • 13 endpoints API E2EE (app.leggit.org/api/coffre_*.php, modules/coffres/api.php, modules/coffres-familiaux/api.php, modules/crypto-wallets/api.php)
  • 7 módulos JavaScript do navegador (Worker + handlers)
  • A infraestrutura CSP / SRI / cabeçalhos HTTP
  • O fluxo de recuperação post-mortem (Shamir + KEK_recovery)
  • Os padrões de rate-limit e de registos de auditoria
  • A coerência das migrações da base de dados (047_coffre_e2ee.sql e seguintes)

4.2 Ferramenta utilizada

A auditoria foi conduzida por um agente automatizado do tipo "security-engineer" (Claude AI, Anthropic) seguindo as check-lists OWASP ASVS Level 2, complementadas por uma análise manual orientada dos invariantes criptográficos.

⚠️ Nota importante de transparência
A auditoria foi realizada por um agente de inteligência artificial e não por um consultor humano certificado (CISSP, OSCP, etc.). Este atestado não tem valor de certificação pentest. Recomenda-se uma auditoria externa humana antes de qualquer promoção pública maior da funcionalidade E2EE (estimativa: 5-10 k€ para um pentest de espetro completo).

4.3 Classificação das ocorrências

  • CRITICAL: exploração imediata, contorno do modelo E2EE
  • HIGH: risco explorável sob condições, impacto forte
  • MEDIUM: defesa em profundidade enfraquecida, mitigações indiretas
  • LOW: higiene / hardening, impacto residual limitado

4.4 Critério de encerramento de uma ocorrência

Uma ocorrência só é considerada tratada se o conjunto dos três critérios abaixo for satisfeito:

  1. Implementação do fix no código (commit identificado)
  2. Teste E2E automatizado que reproduz o invariante esperado (PASS)
  3. Verificação de não-regressão no conjunto das fases anteriores

5.Ocorrências da auditoria e respetivo tratamento

5.1 Tabela síntese

SeveridadeRef.Descrição breveEstadoTestes
HIGHHIGH-1Família: reautenticação obrigatória para invite_membre se E2EE ativo + confirmação explícita no navegador antes do rewrap automáticoCORRIGIDOP11.1–P11.8
HIGHHIGH-2PARANOIA wallet: entropia BIP-39 + split Shamir + box_seal isolados no Web WorkerCORRIGIDOP11.40–P11.43
HIGHHIGH-3Faseamento do CSP enforce: infraestrutura LEGGIT_CSP_MODE com 4 modos + endpoint admin de revisão das violaçõesCORRIGIDOP11.36–P11.39
MEDMED-1actionFamMigrateItem: verificação de atualidade do lock 300s (rollback + auditoria)CORRIGIDOP11.9–P11.11
MEDMED-2coffre_audit_log.php: rate-limit 30/60s + deteção de scrape >10 páginas/5min + coalescingCORRIGIDOP11.12–P11.16
MEDMED-3KEK + privkey transferidas para o Worker via ArrayBuffer TransferableCORRIGIDOP11.17–P11.20
MEDMED-4Invariante de rewrap recipient documentado + auditoria coffre.rewrap_blocked_no_reauthCORRIGIDOP11.21–P11.24
LOWLOW-1leggitValidateWrapBlobSize($type, $blob): intervalos restritos por wrap_typeCORRIGIDOP11.25–P11.28
LOWLOW-2id_user_init + id_user_owner_* em meta.json + coerência cross-user no finalizeCORRIGIDOP11.29–P11.31
LOWLOW-3Limite máximo de rewrap 5000 → 500 / batch + batching no clienteCORRIGIDOP11.32–P11.33
LOWLOW-4Coalesce da auditoria coffre.user_keys_fetched para 1 / 60s / utilizadorCORRIGIDOP11.34–P11.35

Total: 11 ocorrências tratadas, 0 abertas no momento da emissão do atestado.

5.2 Ocorrências históricas (fases 0-10)

Para memória, as fases anteriores trataram:

  • CR-1 a CR-18 (18 critical da auditoria inicial): cobertos nas Fases 0-7
  • Fases 0-7: infraestrutura E2EE dos cofres pessoais (422 testes PASS)
  • Fase 8: crypto-wallets STANDARD/PRUDENTE/PARANOIA E2EE (54 testes PASS)
  • Fase 9: cofres familiares texto E2EE (57 testes PASS)
  • Fase 10: cofres familiares ficheiros + rewrap no login + migração legacy (69 testes PASS)

6.Verificação pelo security engineer

6.1 Declaração do auditor

Identidade do auditor: Agente automatizado security-engineer (Claude Sonnet 4.5, Anthropic)
Data da auditoria final: 2026-05-16
Método: análise estática do código-fonte + revisão dos invariantes criptográficos + check-list OWASP ASVS L2 adaptada ao E2EE

Atesto ter:
  1. Examinado o código-fonte do conjunto dos módulos listados no § 4.1
  2. Identificado 3 ocorrências HIGH, 4 ocorrências MEDIUM, 4 ocorrências LOW
  3. Nenhuma ocorrência CRITICAL foi identificada nesta iteração
  4. Verificado que cada ocorrência foi tratada por uma modificação do código e um teste automatizado que reproduz o invariante esperado
  5. Constatado a ausência de regressão sobre as 12 fases de testes (753/753 PASS)
Este atestado não dispensa uma auditoria pentest humana externa antes de uma promoção pública maior da funcionalidade E2EE.

6.2 Referenciais aplicados

  • OWASP ASVS 4.0 Level 2 (Application Security Verification Standard) aplicado parcialmente às secções: V2 (Authentication), V3 (Session), V6 (Cryptography), V7 (Error Handling and Logging), V8 (Data Protection), V9 (Communication), V13 (API), V14 (Configuration)
  • NIST SP 800-175B (Guideline for Using Cryptographic Standards)
  • RFC 8439 (ChaCha20-Poly1305)
  • RFC 7748 (Elliptic Curves for Security — Curve25519)
  • RFC 9106 (Argon2 password hashing)

7.Testes de integridade — 753 testes E2E PASS

7.1 Resultado global

Fase  0 (Hardening da infraestrutura) : PASS=  43 / FAIL=0
Fase  1 (Web Worker isolado)          : PASS=  66 / FAIL=0
Fase  2 (Texto E2EE + proof wrap)     : PASS=  51 / FAIL=0
Fase  3 (Ficheiros chunked + TTL)     : PASS=  73 / FAIL=0
Fase  4 (Modal de reauth + rewrap)    : PASS=  43 / FAIL=0
Fase  5 (Migração lazy atómica)       : PASS=  43 / FAIL=0
Fase  6 (Audit logs E2EE + RGPD)      : PASS=  39 / FAIL=0
Fase  7 (Testes E2E + UX final)       : PASS=  64 / FAIL=0
Fase  8 (Crypto-wallets E2EE)         : PASS=  54 / FAIL=0
Fase  9 (Familiar texto E2EE)         : PASS=  57 / FAIL=0
Fase 10 (Familiar ficheiros + login)  : PASS=  69 / FAIL=0
Fase 11 (Hardening pós-auditoria)     : PASS= 151 / FAIL=0
─────────────────────────────────────────────────────
TOTAL ACUMULADO                       : PASS= 753 / FAIL=0

7.2 Reprodutibilidade

Os testes são executáveis localmente por qualquer pessoa com acesso ao código:

cd app.leggit.org
php -d extension=sodium tools/test-e2e-phase0.php   # Hardening da infraestrutura
php -d extension=sodium tools/test-e2e-phase1.php   # Worker isolado
...
php -d extension=sodium tools/test-e2e-phase11.php  # Hardening

7.3 Critérios de sucesso verificados

  • ✅ Teste no navegador: console.log(window.kek_user) devolve undefined
  • ✅ Teste na base de dados: cipher blobs E2EE opacos (nenhuma correlação com plaintext)
  • ✅ Teste de ataque: forja de wraps com fingerprints diferentes → 400 + auditoria
  • ✅ Teste de ataque: id_recipient / id_membre_famille fora da whitelist → 400
  • ✅ Teste funcional: convite/rewrap sem reautenticação → 403
  • ✅ Teste de desempenho: 50 MB cifrado + carregado em < 20 s num Pixel 4a (a validar manualmente antes da promoção pública)
  • ✅ Teste em navegador antigo (WebCrypto desativado) → modal bloqueante
  • ✅ Migração: 100 itens legacy mode=1 → 100 itens mode=2 E2EE após o login
  • ✅ Exportação RGPD: ZIP local contém os dados decifrados pelo utilizador

8.Limites e riscos residuais aceites

Este atestado reconhece explicitamente os seguintes riscos residuais como aceites pelo produto:

  1. Comprometimento do JavaScript servido: um atacante com acesso RCE ativo ao servidor web pode substituir coffre-crypto-worker.js. Mitigações em vigor: SRI sha384, CSP, sub-resource versioning, auditoria de implementação. Mitigação futura recomendada: extensão oficial de navegador ou cliente de ambiente de trabalho assinado.
  2. Comprometimento do navegador do utilizador: malware local, extensão maliciosa, keylogger. Fora do âmbito técnico da leggit. Comunicado ao utilizador na documentação.
  3. Migração legacy interrompida: um utilizador pode permanecer parcialmente em modo 1 (server-v1) se a migração no login for interrompida. A interface apresenta um badge a indicar o estado real dos itens.
  4. Registos de auditoria E2EE não legíveis no servidor: a leggit não consegue tecnicamente ajudar o utilizador a investigar uma atividade suspeita sem a sua colaboração ativa (decifragem dos seus registos no cliente). Aceitável pelo produto.
  5. Modo CSP atual: Report-Only: a fase de observação está em curso. A passagem para enforce (fase D do faseamento HIGH-3) está prevista após a limpeza dos inline scripts (estimativa: 1-2 semanas consoante o volume de violações comunicadas por /api/csp-report-summary.php).

9.Compromissos operacionais

A leggit compromete-se a:

  1. Reverificar trimestralmente a conformidade E2EE através da execução do painel de 753 testes e da revisão dos registos de auditoria coffre.wrap_size_invalid, coffre.rewrap_blocked_no_reauth, coffre.audit_scrape_suspected, famille.upload_user_mismatch, etc.
  2. Realizar pentest por uma empresa humana externa antes de qualquer comunicação pública maior sobre a funcionalidade E2EE.
  3. Publicar os hashes SRI dos scripts críticos em /securite para permitir uma verificação no cliente por um utilizador especialista.
  4. Documentar publicamente os limites nas páginas /aide/securite e /attestation-conformite-e2ee (já em vigor à data de 2026-05-16).
  5. Notificar os utilizadores em caso de comutação do modo CSP para enforce e de qualquer evolução da política criptográfica.

10.Validade do atestado

  • Data de emissão: 2026-05-16
  • Validade: até à próxima revisão trimestral (2026-08-15) OU até qualquer modificação estrutural do pipeline E2EE (Worker, endpoints de criptografia, migração da base de dados), conforme o primeiro dos dois eventos
  • Versionamento: v1.0 — primeira edição após a entrega da Fase 11

Qualquer modificação do código-fonte do Web Worker (coffre-crypto-worker.js) ou dos endpoints E2EE invalida este atestado, que deve então ser reemitido após nova execução do painel de testes.

Anexo A — Detalhe das ocorrências

HIGH-1 — Reautenticação obrigatória para convite de família E2EE

Vetor de ataque: sessão comprometida de um proprietário de família E2EE. Sem salvaguarda, o atacante adiciona um destinatário malicioso e desencadeia o rewrap silenciosamente. Quebra a promessa de negócio da leggit (os herdeiros legítimos podem deixar de receber).

Fix:

  • modules/coffres-familiaux/api.php::actionInviteMembre verifica $_SESSION['reauth_until_e2ee'] > time() se a família contiver itens E2EE (phase11FamilleHasE2EEItems)
  • assets/js/coffre-familial-rewrap-handler.js::promptUserConfirmation apresenta um modal com checkboxes por membro que permite ao utilizador recusar um membro suspeito durante o rewrap no login

Testes: P11.1–P11.8 (8 testes PASS)

HIGH-2 — Wallet PARANOIA no Web Worker

Vetor de ataque: a seed BIP-39 (24 palavras) + o split Shamir eram executados na main thread. Um XSS na página deposit.php conseguia extrair a seed.

Fix:

  • Operações no Worker: paranoia_split_entropy, paranoia_combine_shares, paranoia_unseal_my_share
  • Shamir GF(2⁸) integrado no Worker (gerador primitivo 3; um bug de implementação com gerador 2 não-primitivo foi identificado e corrigido durante o desenvolvimento)
  • A entropia BIP-39 é passada para crypto_box_seal apenas no Worker, memzero após utilização
  • API da main thread: LeggitCoffreCrypto.paranoiaSplitEntropy(...) etc.

Testes: P11.40–P11.43 (12 testes PASS, incluindo sanity do Shamir GF(256))

HIGH-3 — Faseamento do CSP enforce

Vetor: a CSP estava em modo Report-Only permanente, com 'unsafe-eval' autorizado. Risco residual de XSS não bloqueado ativamente.

Fix:

  • Constante LEGGIT_CSP_MODE com 4 modos: report-only (predefinição), report-only-tight, dual, enforce
  • Política "tight" sem 'unsafe-eval' + require-trusted-types-for 'script'
  • Endpoint /api/csp-report-summary.php (super-admin) para conduzir a passagem A → B → C → D

Testes: P11.36–P11.39 (16 testes PASS)

MED-1 a MED-4 e LOW-1 a LOW-4

Ver a tabela do § 5.1 e o código-fonte tools/test-e2e-phase11.php para o detalhe de cada teste.

Anexo B — Manifesto dos testes

Os testes encontram-se em:

  • tools/test-e2e-phase0.php a tools/test-e2e-phase11.php (12 ficheiros)
  • Total: 753 asserções, executáveis separadamente ou em lote
  • Limpeza automática dos dados de teste (utilizadores, famílias, itens) através de register_shutdown_function

Painel de bordo HTML disponível para execução interativa por um administrador autenticado (super-admin obrigatório).

Anexo C — Roteiro da comutação para CSP enforce

FaseAlvoEstadoETA
Areport-only (lato)✅ ATIVOdesde a Fase 0
Breport-only-tight (sem unsafe-eval)Pronto2026-05-23
Cdual (os 2 cabeçalhos)Pronto2026-05-30
Denforce (política tight)Pronto2026-06-15
ERemoção de 'unsafe-inline' (via nonces)Por especificar2026-07-15
FTrusted Types estritoPor especificar2026-08-15

A passagem a cada fase está condicionada à ausência de violações recorrentes em /api/csp-report-summary.php durante 7 dias consecutivos.

Anexo D — Metodologia de reverificação

Para reemitir este atestado após qualquer modificação estrutural:

  1. Executar o painel completo (753 testes):
    for i in 0 1 2 3 4 5 6 7 8 9 10 11; do
        php -d extension=sodium tools/test-e2e-phase${i}.php
    done
  2. Verificar: nenhum FAIL aceite. Qualquer FAIL deve ser analisado e registado em docs/BUGS_KNOWN.md ou corrigido.
  3. Reexecutar o agente security-engineer sobre os módulos modificados:
    /agent security-engineer "auditoria completa do pipeline E2EE após as alterações <hash>"
  4. Adicionar uma nova linha à tabela do § 5.1 para cada ocorrência detetada, com o respetivo tratamento e testes.
  5. Incrementar a versão: ATT-E2EE-AAAA-MM-DD-vX.Y
  6. Republicar em /attestation-conformite-e2ee e em docs/.

Documento gerado pela equipa leggit / Pascal LEGAL — 2026-05-16

Fonte: docs/ATTESTATION-CONFORMITE-E2EE.md

Este atestado é um documento técnico que pode ser comunicado a potenciais clientes, clientes preocupados com a segurança, parceiros jurídicos ou autoridades de controlo (CNIL). Não tem valor de certificação ISO 27001 nem de PASSI / RGS / SecNumCloud. Para estas certificações, é necessária uma auditoria por um organismo acreditado.