Verificação de Integridade
O PAM oferece três níveis de verificação de integridade: content hashes por memória, blocos de integridade no nível do arquivo e assinaturas criptográficas opcionais.
Content Hash por Memória
Seção intitulada “Content Hash por Memória”Cada memória inclui um campo content_hash contendo o hash SHA-256 do seu conteúdo normalizado.
Etapas de verificação
Seção intitulada “Etapas de verificação”- Pegue o valor do campo
content - Normalize-o conforme a spec §6:
- Remova espaços em branco no início e no final
- Converta para letras minúsculas
- Aplique a normalização Unicode NFC
- Reduza espaços em branco consecutivos para um único espaço
- Calcule o digest hexadecimal SHA-256 do resultado codificado em UTF-8
- Compare com o
content_hasharmazenado (formato:sha256:<hex>)
Exemplo
Seção intitulada “Exemplo”Conteúdo: "User is a cloud infrastructure engineer"
Após normalização: "user is a cloud infrastructure engineer"
Hash esperado: sha256:e1bae3ec291c99eced01fc91b4152a0cef541fccf2034fc11b3f90f4e4d79b6e
Implementação
Seção intitulada “Implementação”import hashlibimport unicodedata
def verify_content_hash(content: str, expected_hash: str) -> bool: text = content.strip().lower() text = unicodedata.normalize("NFC", text) text = " ".join(text.split()) computed = f"sha256:{hashlib.sha256(text.encode('utf-8')).hexdigest()}" return computed == expected_hashimport {createHash} from "node:crypto";
function verifyContentHash(content, expectedHash) { let text = content.trim().toLowerCase(); text = text.normalize("NFC"); text = text.replace(/\s+/g, " "); const computed = `sha256:${createHash("sha256").update(text, "utf8").digest("hex")}`; return computed === expectedHash;}Bloco de Integridade no Nível do Arquivo
Seção intitulada “Bloco de Integridade no Nível do Arquivo”O bloco opcional integrity verifica se o array de memórias não foi adulterado. O checksum cobre apenas o array de memórias, não o documento inteiro.
Etapas de verificação
Seção intitulada “Etapas de verificação”Conforme a spec §15:
- Extraia o array
memoriesdo documento - Ordene os objetos de memória pelo campo
id, em ordem crescente - Canonicalize o array ordenado usando a RFC 8785 (JSON Canonicalization Scheme)
- Calcule o SHA-256 dos bytes canônicos em UTF-8
- Compare com
integrity.checksum(formato:sha256:<hex>) - Verifique se
integrity.total_memoriescorresponde ao tamanho do arraymemories
Implementação
Seção intitulada “Implementação”import hashlibfrom jcs import canonicalize # pip install jcs (RFC 8785 compliant)
def verify_integrity(pam_data: dict) -> bool: integrity = pam_data.get("integrity") if not integrity: return True # no integrity block, nothing to verify
memories = pam_data["memories"]
# check count if integrity["total_memories"] != len(memories): return False
# sort by id ascending sorted_memories = sorted(memories, key=lambda m: m["id"])
# canonicalize with RFC 8785 (JCS) # Note: json.dumps with sort_keys is NOT equivalent to RFC 8785. canonical = canonicalize(sorted_memories)
computed = f"sha256:{hashlib.sha256(canonical).hexdigest()}" return computed == integrity["checksum"]import {createHash} from "node:crypto";import canonicalize from "canonicalize"; // RFC 8785
function verifyIntegrity(pamData) { const integrity = pamData.integrity; if (!integrity) return true;
const memories = pamData.memories; if (integrity.total_memories !== memories.length) return false;
// sort by id ascending const sorted = [...memories].sort((a, b) => a.id.localeCompare(b.id));
// canonicalize with RFC 8785 const canonical = canonicalize(sorted); const hash = createHash("sha256").update(canonical, "utf8").digest("hex");
return `sha256:${hash}` === integrity.checksum;}Instale o pacote canonicalize: npm install canonicalize
Assinaturas Criptográficas
Seção intitulada “Assinaturas Criptográficas”O PAM v1.0 suporta assinaturas criptográficas opcionais para verificação de autenticidade e detecção de adulteração por meio do bloco signature (spec §18).
Payload da assinatura
Seção intitulada “Payload da assinatura”A assinatura é calculada sobre um objeto de payload específico, não apenas sobre o checksum de integridade. O payload contém quatro campos, canonicalizados com a RFC 8785:
{ "checksum": "<integrity.checksum>", "export_date": "<export_date>", "export_id": "<export_id>", "owner_id": "<owner.id>"}Isso previne ataques de replay — um checksum válido de uma exportação não pode ser reutilizado em uma exportação diferente com um ID ou data diferentes.
Algoritmos suportados
Seção intitulada “Algoritmos suportados”| Algoritmo | Observações |
|---|---|
Ed25519 | Recomendado. Rápido, chaves pequenas, resistente a ataques de canal lateral |
ES256 | ECDSA com curva P-256 |
ES384 | ECDSA com curva P-384 |
RS256 | RSA com SHA-256 |
RS384 | RSA com SHA-384 |
RS512 | RSA com SHA-512 |
Etapas de verificação
Seção intitulada “Etapas de verificação”- Extraia o bloco
signature - Reconstrua o payload:
{checksum, export_date, export_id, owner_id} - Canonicalize o payload com a RFC 8785
- Verifique a assinatura usando
signature.algorithm,signature.public_keyesignature.value(codificado em Base64url conforme a RFC 4648 §5) - Se
owner.didestiver presente, opcionalmente resolva o documento DID e verifique sesignature.public_keycorresponde a um método de verificação no documento