Documentos técnicos

Documentação técnica detalhada da arquitetura criptográfica do PDF Pro

Publicado: 16 de abril de 2025 Última atualização: 16 de abril de 2026 Versão: 2.0

Índice de whitepapers

  1. Transferência de ficheiros encriptada de ponta a ponta
  2. Assinatura criptográfica de documentos com privacidade em primeiro lugar

Transferência de ficheiros encriptada de ponta a ponta

PDF Pro Whitepaper WP-001 — Arquitetura e segurança

1.1 Resumo

Este whitepaper descreve a arquitetura do sistema Transferência Segura do PDF Pro, um mecanismo de transferência de ficheiros encriptado de ponta a ponta no qual os ficheiros são encriptados do lado do cliente com AES-256-GCM antes do envio. O servidor armazena apenas texto cifrado opaco e não tem capacidade para desencriptar, inspecionar ou ler o conteúdo dos ficheiros em qualquer momento. O sistema suporta dois modos de chave: uma chave aleatória gerada automaticamente (transportada no fragmento do URL, que nunca é enviado ao servidor) ou uma chave derivada de uma frase-passe usando PBKDF2. Em ambos os modos, a chave de encriptação existe apenas no navegador do remetente e do destinatário. Este documento detalha as primitivas criptográficas, o fluxo de dados, o modelo de ameaças e as limitações honestas do sistema. Nota sobre a terminologia: este sistema não utiliza provas de conhecimento zero (ZKPs). Quando dizemos que o servidor tem "conhecimento zero" dos seus ficheiros, queremos dizer que — como consequência da encriptação do lado do cliente — o servidor apenas detém texto cifrado que não consegue desencriptar.

1.2 Visão geral da arquitetura

O sistema Transferência Segura segue uma separação rigorosa entre cliente e servidor, na qual todas as operações criptográficas ocorrem exclusivamente no navegador:

O servidor é intencionalmente concebido como um "tubo opaco" para dados encriptados. Não tem conhecimento da chave de encriptação, da frase-passe ou do conteúdo dos ficheiros. Isto é imposto arquitetonicamente, não meramente por política.

Princípio de design: O servidor deve poder ser comprometido sem comprometer os dados dos utilizadores. Mesmo com acesso total à base de dados e execução de código do lado do servidor, um atacante não consegue desencriptar os ficheiros transferidos.

1.3 Encriptação: AES-256-GCM via Web Crypto API

Toda a encriptação de ficheiros utiliza o algoritmo de encriptação autenticada AES-256-GCM, acedido através da Web Crypto API nativa do navegador. Isto fornece tanto confidencialidade (os dados não podem ser lidos) como integridade (os dados não podem ser modificados sem deteção).

1.3.1 Parâmetros do Algoritmo

ParâmetroValorJustificação
AlgoritmoAES-256-GCMAprovado pelo NIST, encriptação autenticada com dados associados (AEAD)
Tamanho da chave256 bitsComprimento máximo de chave AES; fornece segurança de 128 bits contra força bruta
Tamanho do IV96 bits (12 bytes)Comprimento de IV recomendado pelo NIST para o modo GCM
Tamanho do tag128 bits (16 bytes)Tag de autenticação de comprimento total para máxima proteção da integridade
Geração do IVcrypto.getRandomValues()Gerador de números aleatórios criptograficamente seguro

1.3.2 Implementação da Encriptação

// Fluxo de encriptação simplificado (Web Crypto API)

const iv = crypto.getRandomValues(new Uint8Array(12));
const salt = crypto.getRandomValues(new Uint8Array(16));

// Derivar chave a partir da frase-passe (ver Secção 1.4)
const key = await deriveKey(passphrase, salt);

// Encriptar o ficheiro
const ciphertext = await crypto.subtle.encrypt(
  { name: "AES-GCM", iv: iv },
  key,
  fileArrayBuffer
);

// Pacote: [salt (16B)] + [iv (12B)] + [ciphertext + tag]
const blob = concatenate(salt, iv, ciphertext);

O blob encriptado final é uma concatenação de três componentes: o salt de 16 bytes (utilizado para derivação de chave), o IV de 12 bytes (utilizado para AES-GCM) e o texto cifrado com o tag de autenticação GCM de 16 bytes anexado. Este blob é o que o servidor recebe e armazena.

1.4 Geração e transporte de chaves

A Transferência Segura suporta dois modos de chave mutuamente exclusivos. O modo é selecionado pelo remetente no momento da criação da transferência.

1.4.1 Modo A — Chave Gerada Automaticamente (Predefinição)

No modo predefinido, não é envolvida qualquer frase-passe. O navegador gera diretamente uma chave AES de 256 bits criptograficamente aleatória:

1.4.2 Modo B — Transferência Protegida por Frase-Passe

Quando o remetente opta por definir uma frase-passe, a chave é derivada dessa frase-passe utilizando PBKDF2:

1.4.3 Parâmetros PBKDF2 (Modo B)

ParâmetroValorJustificação
AlgoritmoPBKDF2Recomendado pelo NIST SP 800-132; amplamente auditado
Função de hashSHA-256Hash criptográfico padrão; saída de 256 bits
Iterações600 000Cumpre a recomendação OWASP 2023 para PBKDF2-SHA256
Tamanho do salt128 bits (16 bytes)Único por transferência; previne ataques por tabelas arco-íris
Comprimento da chave de saída256 bitsCorresponde ao requisito de chave AES-256

1.4.4 Implementação da Derivação de Chave (Modo B)

async function deriveKey(passphrase, salt) {
  // Importar frase-passe como material de chave em bruto
  const keyMaterial = await crypto.subtle.importKey(
    "raw",
    new TextEncoder().encode(passphrase),
    { name: "PBKDF2" },
    false,
    ["deriveKey"]
  );

  // Derivar chave AES-256-GCM
  return crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt: salt,
      iterations: 600000,
      hash: "SHA-256"
    },
    keyMaterial,
    { name: "AES-GCM", length: 256 },
    false,
    ["encrypt", "decrypt"]
  );
}

Nota de segurança: A segurança depende da entropia da frase-passe no Modo B. Recomendamos frases-passe com 12 ou mais caracteres misturando maiúsculas, minúsculas, dígitos e símbolos.

1.5 Fluxo de dados

O ciclo de vida completo de uma transferência segura segue este percurso:

1.5.1 Envio (Upload) — Modo A (Chave Gerada Automaticamente)

  1. O remetente seleciona um ficheiro no navegador.
  2. O navegador gera uma chave AES-GCM de 256 bits aleatória e um IV de 12 bytes aleatório utilizando crypto.getRandomValues().
  3. O navegador encripta o ficheiro utilizando AES-256-GCM, produzindo texto cifrado + tag de autenticação.
  4. O navegador concatena [IV | ciphertext+tag] num único blob.
  5. O navegador envia o blob encriptado para o servidor através de HTTPS.
  6. O servidor armazena o blob no Supabase Storage e cria um registo de metadados (ID da transferência, expiração, limite de transferências, nome do ficheiro, tamanho do ficheiro).
  7. O servidor devolve um URL de transferência. O navegador anexa a chave exportada ao fragmento do URL (#).
  8. O remetente partilha o URL completo (com fragmento) com o destinatário.

1.5.2 Envio (Upload) — Modo B (Frase-Passe)

  1. O remetente seleciona um ficheiro e introduz uma frase-passe no navegador.
  2. O navegador gera um salt aleatório de 16 bytes e um IV de 12 bytes utilizando crypto.getRandomValues().
  3. O navegador deriva uma chave AES-256 a partir da frase-passe utilizando PBKDF2 (600 mil iterações).
  4. O navegador encripta o ficheiro utilizando AES-256-GCM, produzindo texto cifrado + tag de autenticação.
  5. O navegador concatena [salt | IV | ciphertext+tag] num único blob.
  6. O navegador envia o blob encriptado para o servidor através de HTTPS.
  7. O servidor armazena o blob no Supabase Storage e cria um registo de metadados (ID da transferência, expiração, limite de transferências, nome do ficheiro, tamanho do ficheiro).
  8. O servidor devolve um URL de transferência contendo o ID da transferência.
  9. O remetente partilha o URL de transferência e a frase-passe com o destinatário através de canais separados.

1.5.3 Receção (Download) — Modo A

  1. O destinatário abre o URL de transferência completo (com a chave no fragmento).
  2. O navegador extrai a chave AES do fragmento do URL (nunca enviado ao servidor).
  3. O navegador descarrega o blob encriptado do servidor através de HTTPS.
  4. O navegador extrai o IV (primeiros 12 bytes) do blob.
  5. O navegador desencripta o texto cifrado utilizando AES-256-GCM com a chave e o IV extraídos.
  6. Se a desencriptação for bem-sucedida (o tag GCM valida), o ficheiro em texto simples é apresentado ao utilizador.
  7. O servidor atualiza o contador de transferências e, se a opção "queimar após leitura" estiver ativa, elimina o blob.

1.5.4 Receção (Download) — Modo B

  1. O destinatário abre o URL de transferência e introduz a frase-passe no navegador.
  2. O navegador descarrega o blob encriptado do servidor através de HTTPS.
  3. O navegador extrai o salt (primeiros 16 bytes) e o IV (12 bytes seguintes) do blob.
  4. O navegador deriva a chave AES-256 a partir da frase-passe + salt utilizando PBKDF2 (600 mil iterações).
  5. O navegador desencripta o texto cifrado utilizando AES-256-GCM com a chave derivada e o IV.
  6. Se a desencriptação for bem-sucedida (o tag GCM valida), o ficheiro em texto simples é apresentado ao utilizador.
  7. Se a desencriptação falhar (frase-passe errada = chave errada = tag GCM inválido), é apresentado um erro.
  8. O servidor atualiza o contador de transferências e, se a opção "queimar após leitura" estiver ativa, elimina o blob.

1.6 O que o servidor armazena vs. o que nunca vê

Elemento de DadosServidor Tem Acesso?Detalhes
Blob encriptado (salt + IV + ciphertext + tag)SimDados binários opacos; o servidor não consegue interpretar o conteúdo
ID da transferênciaSimOs identificadores de transferência são valores UUID v4 gerados pela função gen_random_uuid() do PostgreSQL, fornecendo 122 bits de aleatoriedade criptográfica do CSPRNG do servidor
Nome de ficheiro originalSimArmazenado em metadados para apresentação ao destinatário
Tamanho de ficheiro originalSimArmazenado em metadados para fins de apresentação
Carimbo temporal de expiraçãoSimUtilizado para aplicação de eliminação automática
Contagem / limite de transferênciasSimUtilizado para aplicação de "queimar após leitura" e do limite de transferências
ID do utilizador remetente (se autenticado)SimLiga a transferência à conta para gestão
Frase-passeNão — nuncaNunca enviada ao servidor; nunca sai do navegador
Chave de encriptação derivadaNão — nuncaExiste apenas na memória do navegador durante a encriptação/desencriptação
Conteúdo do ficheiro em texto simplesNão — nuncaApenas o texto cifrado encriptado chega ao servidor
Número de iterações PBKDF2NãoCodificado no cliente; não transmitido ao servidor

1.7 Expiração automática e Burn-on-Read

Todas as transferências seguras são efémeras por design. Não existe opção de armazenamento permanente.

1.7.1 Opções de Expiração

OpçãoComportamentoAplicação
Queimar após leituraO blob encriptado é eliminado imediatamente após a primeira transferência bem-sucedidaDo lado do servidor: blob eliminado do armazenamento após o stream de transferência terminar
1 horaEliminado automaticamente após 1 hora, independentemente do estado de transferênciaDo lado do servidor: tarefa de limpeza agendada + verificação de expiração no acesso
24 horasEliminado automaticamente após 24 horasIgual ao anterior
7 diasRetenção máxima; eliminado automaticamente após 7 diasIgual ao anterior

1.7.2 Garantia de Eliminação

Quando uma transferência expira ou é queimada após leitura, o blob encriptado é eliminado permanentemente do Supabase Storage. O registo de metadados é mantido durante 30 dias num estado de eliminação suave (para investigação de abuso) antes de ser purgado permanentemente. O registo de metadados não contém a chave de encriptação, a frase-passe ou qualquer informação que possa ser utilizada para reconstruir o ficheiro.

1.8 Modelo de ameaças e mitigações

AmeaçaVetor de AtaqueMitigação
Comprometimento do servidor Atacante obtém acesso total à base de dados e ao armazenamento Todos os dados armazenados são texto cifrado AES-256-GCM. O atacante obtém apenas blobs opacos. Sem a frase-passe, atacar AES de 256 bits por força bruta é computacionalmente inviável.
Interceção de rede (MITM) Atacante interceta dados em trânsito Todo o tráfego utiliza TLS 1.3. Mesmo que o TLS seja quebrado, o atacante obtém apenas blobs encriptados (igual ao comprometimento do servidor).
Frase-passe fraca Atacante força por força bruta uma frase-passe curta ou comum O PBKDF2 com 600 mil iterações torna cada tentativa computacionalmente dispendiosa. Uma frase-passe de 4 caracteres ainda exigiria poder de computação significativo. A interface aplica um comprimento mínimo da frase-passe e fornece feedback de robustez.
Interceção da frase-passe Atacante interceta a frase-passe partilhada entre remetente e destinatário A frase-passe é partilhada fora da banda (não através do nosso sistema). Recomendamos a partilha através de um canal diferente do URL de transferência. Esta é uma responsabilidade do utilizador.
Adulteração do código do lado do cliente Atacante modifica o JavaScript servido ao utilizador Todos os recursos são servidos por HTTPS através do CDN da Vercel. Hashes de Subresource Integrity (SRI) protegem contra adulteração ao nível do CDN. Os utilizadores podem verificar o código-fonte nas ferramentas de programador do navegador.
Extração de memória Atacante extrai a chave de encriptação da memória do navegador As chaves da Web Crypto API são marcadas como não extraíveis sempre que possível. As chaves derivadas existem em memória apenas durante a operação de encriptação/desencriptação. O isolamento de memória do navegador fornece proteção ao nível do sistema operativo.
Ataque de repetição Atacante repete um blob encriptado capturado Cada transferência tem um ID único e é protegida por limites de transferência e expiração. As transferências com queima após leitura são eliminadas após o primeiro acesso.

1.8.1 Pressupostos

1.8.2 Fora do Âmbito

1.9 Limitações e divulgação honesta

Limitações honestas: Nenhum sistema de segurança é perfeito. Acreditamos na divulgação transparente das limitações conhecidas.


Assinatura criptográfica de documentos com privacidade em primeiro lugar

PDF Pro Whitepaper WP-002 — Arquitetura e segurança

2.1 Resumo

Este whitepaper descreve a arquitetura do sistema Assinatura Privada do PDF Pro, um mecanismo criptográfico de assinatura de documentos no qual o documento PDF nunca sai do navegador do signatário. O sistema utiliza ECDSA com a curva P-256 e hash SHA-256 para produzir assinaturas digitais separadas. Apenas o hash do documento, a assinatura criptográfica e a chave pública são transmitidos ao servidor. As chaves privadas são geradas, encriptadas e armazenadas exclusivamente no navegador do utilizador através de IndexedDB, protegidas por derivação de chave PBKDF2 (600 000 iterações) e encriptação AES-256-GCM. Este documento detalha a arquitetura completa de assinatura e verificação, o modelo de gestão de chaves, o esquema do payload assinado, o design do registo de auditoria, o modelo de ameaças e as limitações honestas.

2.2 Visão geral da arquitetura

O sistema Assinatura Privada foi concebido em torno de uma restrição fundamental: o documento PDF nunca pode ser transmitido ao servidor. Isto é imposto arquitetonicamente através de um modelo de assinatura separada.

Garantia central: O servidor nunca vê, recebe ou processa o documento PDF. Os únicos dados relacionados com o documento que o servidor recebe são um hash SHA-256 — um valor de tamanho fixo de 256 bits a partir do qual o documento original não pode ser reconstruído.

2.3 Algoritmo de assinatura: ECDSA P-256 / SHA-256

2.3.1 Parâmetros do Algoritmo

ParâmetroValorJustificação
Algoritmo de assinaturaECDSA (Algoritmo de Assinatura Digital de Curva Elíptica)NIST FIPS 186-4; assinaturas compactas; segurança forte por bit de chave
CurvaP-256 (secp256r1 / prime256v1)Aprovada pelo NIST; nível de segurança de 128 bits; amplo suporte na Web Crypto API
Função de hashSHA-256NIST FIPS 180-4; resumo de 256 bits; resistente a colisões
Tamanho da chaveChave privada de 256 bits, chave pública de 512 bits (não comprimida)Padrão para P-256; equivalente a RSA de ~3072 bits
Tamanho da assinatura64 bytes (r: 32 bytes, s: 32 bytes)Compacta; adequada para armazenamento e transmissão
Formato da assinaturaIEEE P1363 (r || s em bruto)Saída nativa da Web Crypto API; codificada em base64url para armazenamento

Codificação da assinatura: O ECDSA com P-256 da Web Crypto API produz uma assinatura em bruto de 64 bytes composta por dois inteiros de 32 bytes (r || s) em formato big-endian de largura fixa. Esta saída em bruto é codificada em base64url para armazenamento. Este NÃO é um formato DER — é o formato IEEE P1363 que a Web Crypto API produz nativamente.

2.3.2 Fluxo de Assinatura

// 1. Calcular hash do documento PDF (do lado do cliente)
const fileBuffer = await file.arrayBuffer();
const hashBuffer = await crypto.subtle.digest("SHA-256", fileBuffer);
const hashHex = Array.from(new Uint8Array(hashBuffer))
  .map(b => b.toString(16).padStart(2, '0')).join('');

// 2. Assinar o hash com a chave privada (do lado do cliente)
const signature = await crypto.subtle.sign(
  { name: "ECDSA", hash: "SHA-256" },
  privateKey,   // CryptoKey de IndexedDB (desencriptada)
  hashBuffer
);

// 3. Enviar ao servidor: hash + assinatura + chave pública (NÃO o PDF)
await submitSignature({
  documentHash: hashHex,
  signature: base64Encode(signature),
  publicKey: exportedPublicKeyJWK
});

2.4 Gestão de chaves: efémeras vs. persistentes

Tipo de ChaveContextoCiclo de VidaArmazenamento
Efémera Utilizadores convidados (sem sessão iniciada) Gerada por sessão; destruída quando o separador é fechado Apenas em memória (objeto CryptoKey); nunca persistida
Persistente Utilizadores autenticados (com sessão iniciada) Gerada uma vez; persistida entre sessões; revogável IndexedDB (encriptada com PBKDF2 + AES-256-GCM)

2.4.1 Geração de Chaves

// Gerar par de chaves ECDSA P-256 (Web Crypto API)
const keyPair = await crypto.subtle.generateKey(
  {
    name: "ECDSA",
    namedCurve: "P-256"
  },
  true,   // extraível (necessário para encriptação + armazenamento)
  ["sign", "verify"]
);

// Exportar chave pública como JWK para registo no servidor
const publicKeyJWK = await crypto.subtle.exportKey("jwk", keyPair.publicKey);

// Exportar chave privada como JWK para armazenamento encriptado
const privateKeyJWK = await crypto.subtle.exportKey("jwk", keyPair.privateKey);

Formato da chave pública: As chaves públicas são exportadas e armazenadas no formato JWK (JSON Web Key). A impressão digital da chave é calculada como SHA-256 do JWK canónico contendo apenas os campos públicos {crv, kty, x, y} com as chaves ordenadas alfabeticamente.

2.5 Proteção da chave privada: PBKDF2 + AES-GCM → IndexedDB

As chaves privadas persistentes nunca são armazenadas em texto simples. Antes de serem escritas em IndexedDB, a chave privada (exportada como JWK JSON) é encriptada utilizando o mesmo padrão da Transferência Segura:

2.5.1 Parâmetros de Proteção

ParâmetroValor
Derivação de chavePBKDF2-SHA256, 600 000 iterações
Salt16 bytes aleatórios (por chave)
EncriptaçãoAES-256-GCM
IV12 bytes aleatórios (por encriptação)
EntradaJWK da chave privada (string JSON, codificada em UTF-8)
Saída armazenada em IndexedDB{ salt, iv, ciphertext, publicKeyJWK, keyId, createdAt }

2.5.2 Esquema de Armazenamento

// Estrutura do registo IndexedDB para uma chave de assinatura encriptada
{
  "keyId":       "uuid-v4-unique-identifier",
  "publicKey":   { /* formato JWK, não encriptado */ },
  "encryptedPrivateKey": {
    "salt":       "base64-encoded-16-bytes",
    "iv":         "base64-encoded-12-bytes",
    "ciphertext": "base64-encoded-aes-gcm-ciphertext"
  },
  "algorithm":   "ECDSA",
  "curve":       "P-256",
  "createdAt":   "2026-04-16T00:00:00.000Z",
  "userId":      "supabase-auth-user-id"
}

Recuperação de chave: Se o utilizador esquecer a sua frase-passe de assinatura, a chave privada não pode ser recuperada. Não temos a frase-passe, a chave derivada ou qualquer mecanismo para contornar a proteção PBKDF2 + AES-GCM. Os utilizadores devem exportar cópias de segurança das chaves.

2.6 Modelo de assinatura destacada

O PDF Pro utiliza um modelo de assinatura separada, o que significa que a assinatura é armazenada separadamente do documento. Este é o mecanismo crítico de privacidade: o documento nunca sai do dispositivo do signatário.

2.6.1 O Que É Enviado ao Servidor

2.6.2 O Que NÃO É Enviado ao Servidor

Garantia criptográfica: O SHA-256 é uma função de hash unidirecional. Dado apenas o hash e3b0c44298fc1c14..., fornece uma forte garantia criptográfica, sob os pressupostos de segurança do ECDSA e do SHA-256, de que o documento original não pode ser reconstruído. O hash não revela nada sobre o conteúdo, comprimento ou estrutura do documento, para além de confirmar a identidade quando recalculado.

2.7 Esquema do payload assinado v1.0

O esquema JSON seguinte define o registo de assinatura completo armazenado no servidor:

{
  "schemaVersion":  "1.0",
  "signatureId":    "uuid-v4",
  "documentHash":   "sha256-hex-64-chars",
  "hashAlgorithm":  "SHA-256",
  "signature":      "base64-encoded-ecdsa-signature",
  "signatureAlgorithm": "ECDSA",
  "curve":          "P-256",
  "publicKey": {
    "kty": "EC",
    "crv": "P-256",
    "x":   "base64url-encoded-x-coordinate",
    "y":   "base64url-encoded-y-coordinate"
  },
  "signer": {
    "identityLevel": "authenticated | self-asserted",
    "displayName":   "string or null",
    "email":         "string or null",
    "userId":        "supabase-uid or null"
  },
  "timestamp":      "ISO-8601-UTC",
  "metadata": {
    "fileName":     "original-file-name.pdf",
    "fileSize":     123456,
    "pageCount":    12,
    "clientVersion": "2.0.0",
    "userAgent":    "browser-user-agent-string"
  },
  "auditChain": {
    "previousEventHash": "sha256-of-previous-audit-event or null",
    "eventHash":         "sha256-of-this-record"
  }
}

2.8 Fluxo de verificação

A verificação de assinaturas é um processo em duas fases: verificação criptográfica do lado do cliente seguida de verificação cruzada do lado do servidor.

2.8.1 Fase 1: Verificação Criptográfica do Lado do Cliente

  1. O utilizador carrega um PDF no navegador.
  2. O navegador calcula o hash SHA-256 do PDF carregado.
  3. O navegador consulta o servidor por quaisquer registos de assinatura que correspondam a este hash.
  4. Para cada registo de assinatura devolvido, o navegador efetua a verificação ECDSA:
    const isValid = await crypto.subtle.verify(
      { name: "ECDSA", hash: "SHA-256" },
      importedPublicKey,
      signatureBuffer,
      hashBuffer
    );
  5. Se isValid === true, a assinatura é criptograficamente válida: o documento não foi modificado desde a assinatura e a assinatura foi produzida pelo detentor da chave privada correspondente.

2.8.2 Fase 2: Verificação Cruzada do Lado do Servidor

  1. O servidor confirma que a chave pública no registo de assinatura está registada num utilizador conhecido (para assinaturas autenticadas).
  2. O servidor confirma que o registo de assinatura não foi revogado.
  3. O servidor confirma a integridade do registo de auditoria (validação da cadeia de hashes).
  4. O servidor devolve o nível de identidade do signatário e o estado de registo.

2.8.3 Resultados da Verificação

ResultadoSignificado
Válida (Autenticada)A assinatura é criptograficamente válida E a chave pública pertence a um utilizador PDF Pro registado e autenticado.
Válida (Auto-Declarada)A assinatura é criptograficamente válida, mas a identidade do signatário é auto-declarada (utilizador convidado ou nome não verificado).
InválidaA verificação criptográfica falhou. O documento foi modificado desde a assinatura ou a assinatura está corrompida.
RevogadaA assinatura era válida, mas foi explicitamente revogada pelo signatário.
Nenhuma assinatura encontradaNão existe registo de assinatura para este hash de documento.

2.9 Registo de auditoria: eventos em cadeia de hash

Cada evento de assinatura e verificação é registado num registo de auditoria à prova de adulteração. Os eventos são encadeados por hash: cada evento inclui o hash SHA-256 do evento anterior, formando uma cadeia apenas de adição semelhante a uma blockchain.

2.9.1 Tipos de Eventos de Auditoria

Tipo de EventoAcionadorDados Registados
KEY_REGISTEREDUtilizador regista uma nova chave públicaJWK da chave pública, ID do utilizador, carimbo temporal
DOCUMENT_SIGNEDUtilizador assina um documentoHash do documento, assinatura, chave pública, identidade do signatário, carimbo temporal
SIGNATURE_VERIFIEDQualquer utilizador verifica uma assinaturaHash do documento, resultado da verificação, informação do verificador (se autenticado), carimbo temporal
SIGNATURE_REVOKEDSignatário revoga uma assinaturaID da assinatura, motivo da revogação, carimbo temporal
KEY_REVOKEDUtilizador revoga uma chave públicaID da chave pública, motivo da revogação, carimbo temporal

2.9.2 Estrutura da Cadeia de Hashes

// Cada evento de auditoria inclui:
{
  "eventId":            "uuid-v4",
  "eventType":          "DOCUMENT_SIGNED",
  "timestamp":          "ISO-8601-UTC",
  "data":               { /* payload específico do evento */ },
  "previousEventHash": "sha256-of-previous-event-json",
  "eventHash":          "sha256-of-this-event-json-without-eventHash"
}

// Deteção de adulteração: para verificar a cadeia, calcular:
// SHA-256(JSON.stringify(evento sem o campo eventHash))
// e confirmar que corresponde ao eventHash.
// Depois confirmar que previousEventHash corresponde ao eventHash do evento anterior.

Se algum evento na cadeia for modificado, todas as ligações de hash subsequentes serão quebradas, tornando a adulteração imediatamente detetável. Isto fornece uma forte garantia da integridade do registo de auditoria.

Limitação importante: A cadeia de hashes torna a adulteração detetável dentro da sequência de eventos registados. No entanto, um administrador da base de dados com acesso direto poderia teoricamente eliminar e reconstruir a cadeia. Garantias mais fortes exigiriam carimbo temporal externo ou atestação por terceiros, o que não está implementado nesta versão.

2.10 Níveis de identidade

NívelRequisitosPropriedades de ConfiançaCaso de Uso
Autenticada Utilizador PDF Pro com sessão iniciada e e-mail verificado; par de chaves persistente registado na conta E-mail verificado pelo Supabase Auth; chave pública vinculada a uma conta autenticada; registo de auditoria ligado ao ID do utilizador Documentos empresariais, contratos, acordos formais
Auto-Declarada Utilizador convidado ou utilizador autenticado com nome introduzido por si próprio; par de chaves efémero ou persistente Integridade criptográfica garantida; a identidade do signatário é auto-declarada e não é independentemente verificada Assinatura rápida, documentos pessoais, acordos informais

Nota: Na interface do produto, "self_asserted" pode ser apresentado como "Identidade Auto-Declarada" ou "Assinatura de Convidado". "authenticated" pode ser apresentado como "Conta Autenticada". Estas são etiquetas de apresentação para os mesmos níveis de identidade subjacentes.

Vinculação de identidade: Uma assinatura "autenticada" significa que a chave pública está registada numa conta PDF Pro com um e-mail verificado. NÃO significa que a identidade real do signatário foi verificada por documento de identificação governamental, biometria ou verificação presencial. Não realizamos verificações Know Your Customer (KYC).

2.11 Modelo de ameaças

AmeaçaVetor de AtaqueMitigação
Ataque de repetição Atacante copia uma assinatura válida e aplica-a a um documento diferente A assinatura está vinculada ao hash SHA-256 do documento específico. Um documento diferente terá um hash diferente e a verificação ECDSA falhará.
Falsificação Atacante cria uma assinatura válida sem a chave privada A segurança do ECDSA P-256 baseia-se no Problema do Logaritmo Discreto em Curvas Elípticas (ECDLP). Falsificar uma assinatura sem a chave privada é computacionalmente inviável (nível de segurança de 128 bits).
Substituição de chave Atacante regista a sua própria chave pública e alega que uma assinatura foi feita por outra pessoa As assinaturas autenticadas vinculam a chave pública a um e-mail verificado. O registo de auditoria regista qual a chave que assinou qual documento. Os eventos de registo de chave são encadeados por hash.
Substituição de documento Atacante modifica um PDF assinado e alega que a assinatura ainda é válida Qualquer modificação ao PDF altera o seu hash SHA-256. A assinatura existente falhará a verificação contra o novo hash. Encontrar uma colisão (documento diferente com o mesmo hash) requer ~2^128 operações.
Roubo da chave privada Atacante extrai a chave privada encriptada de IndexedDB A chave privada é encriptada com AES-256-GCM, com chave derivada por PBKDF2 (600 mil iterações). Sem a frase-passe, desencriptar a chave é computacionalmente inviável.
Comprometimento do servidor Atacante obtém acesso total ao servidor O servidor tem apenas chaves públicas e registos de assinaturas. As chaves privadas nunca chegam ao servidor. Um atacante não consegue falsificar novas assinaturas. Pode eliminar ou modificar registos existentes, mas as quebras na cadeia de hashes seriam detetáveis.
Adulteração do registo de auditoria Atacante modifica eventos do registo de auditoria no servidor Eventos encadeados por hash: modificar qualquer evento quebra a cadeia a partir desse ponto. Ferramentas de verificação independentes podem detetar quebras na cadeia.

2.11.1 Pressupostos

2.11.2 Fora do Âmbito

2.12 Divulgação honesta

O que as assinaturas do PDF Pro NÃO são:

2.12.1 Para Que São Adequadas as Assinaturas do PDF Pro

O nosso compromisso: Construímos segurança através da arquitetura, não do marketing. Estes whitepapers descrevem exatamente como os nossos sistemas funcionam, incluindo as suas limitações. Acreditamos que os utilizadores conscientes da segurança merecem total transparência técnica. Se tiver questões sobre qualquer aspeto da nossa arquitetura, contacte-nos em info@webdesign9.com.