Projeto 11: Criptografia End-to-End no Navegador com Web Crypto API 💻
Escopo do Projeto
Objetivo: Desenvolver uma biblioteca de criptografia ponta a ponta (E2EE) em JavaScript cliente utilizando a Web Crypto API nativa do navegador (window.crypto.subtle).
🎯 1. Contexto & Desafio Prático
Aplicações de mensagens seguras e registros médicos exigem que os dados cheguem aos servidores já cifrados, garantindo que mesmo um banco de dados totalmente invadido revele apenas ruído criptográfico aos atacantes (Zero-Knowledge). Você construirá o módulo de criptografia cliente derivando chaves com PBKDF2 e cifrando mensagens via AES-GCM.
📋 2. Requisitos Técnicos Obrigatórios
- R1 (Derivação Segura via PBKDF2): Derivar uma chave criptográfica simétrica a partir da senha do usuário e salt de 16 bytes utilizando PBKDF2 com HMAC-SHA-256 e no mínimo 100.000 iterações.
- R2 (Cifragem AES-GCM Assíncrona): Implementar a cifragem de strings UTF-8 usando
subtle.encryptcom AES-GCM e vetor de inicialização criptográfico de 96 bits (crypto.getRandomValues). - R3 (Decifragem com Validação de Integridade): Implementar a função de decifragem que captura automaticamente falhas de integridade (OperationError) caso os dados cifrados sofram adulteração no canal.
- R4 (Compatibilidade com Padrões Web): Formatar os dados cifrados em formato portátil Base64 para envio transparente via requisições REST JSON ou WebSockets.
📐 3. Diagrama Conceitual & Arquitetura
sequenceDiagram
autonumber
participant Alice as Navegador Alice (Cliente A)
participant Server as Servidor Relé (Não Confiável)
participant Bob as Navegador Bob (Cliente B)
Note over Alice: 1. Derivação de Chave (PBKDF2)<br/>2. Cifragem AES-GCM (Web Crypto)
Alice->>Server: Envia Payload Cifrado {iv, salt, ciphertext} (Zero-Knowledge)
Note over Server: Servidor armazena e repassa sem ler o conteúdo
Server->>Bob: Entrega Payload Cifrado
Note over Bob: 1. Deriva Mesma Chave Compartilhada<br/>2. Decifragem Autenticada via subtle.decrypt
Bob-->>Bob: Mensagem Original Restaurada em Texto Claro! 💻 4. Especificação Técnica & Código de Referência
// e2ee_client.js
// Implementação E2EE utilizando a Web Crypto API nativa
async function deriveKeyFromPassword(password, salt) {
const enc = new TextEncoder();
const baseKey = await window.crypto.subtle.importKey(
"raw", enc.encode(password), { name: "PBKDF2" }, false, ["deriveKey"]
);
return window.crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt: salt,
iterations: 100000,
hash: "SHA-256"
},
baseKey,
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
}
async function encryptMessage(password, plainText) {
const salt = window.crypto.getRandomValues(new Uint8Array(16));
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const key = await deriveKeyFromPassword(password, salt);
const enc = new TextEncoder();
const cipherBuffer = await window.crypto.subtle.encrypt(
{ name: "AES-GCM", iv: iv },
key,
enc.encode(plainText)
);
return {
salt: btoa(String.fromCharCode(...salt)),
iv: btoa(String.fromCharCode(...iv)),
ciphertext: btoa(String.fromCharCode(...new Uint8Array(cipherBuffer)))
};
}
async function decryptMessage(password, envelope) {
const salt = Uint8Array.from(atob(envelope.salt), c => c.charCodeAt(0));
const iv = Uint8Array.from(atob(envelope.iv), c => c.charCodeAt(0));
const cipherData = Uint8Array.from(atob(envelope.ciphertext), c => c.charCodeAt(0));
const key = await deriveKeyFromPassword(password, salt);
const decryptedBuffer = await window.crypto.subtle.decrypt(
{ name: "AES-GCM", iv: iv },
key,
cipherData
);
return new TextDecoder().decode(decryptedBuffer);
}
📦 5. Critérios de Avaliação e Entrega
- Utilização integral das primitivas criptográficas da Web Crypto API moderna.
- Derivação de chaves resistente a ataques de força bruta com PBKDF2 e 100.000 iterações.
- Geração correta de vetores de inicialização com
crypto.getRandomValues.