Pular para conteúdo

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.encrypt com 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

  1. Utilização integral das primitivas criptográficas da Web Crypto API moderna.
  2. Derivação de chaves resistente a ataques de força bruta com PBKDF2 e 100.000 iterações.
  3. Geração correta de vetores de inicialização com crypto.getRandomValues.