Integrity Verification
PAM provides three levels of integrity verification: per-memory content hashes, file-level integrity blocks, and optional cryptographic signatures.
Per-Memory Content Hash
Section titled “Per-Memory Content Hash”Every memory includes a content_hash field containing the SHA-256 hash of its normalized content.
Verification steps
Section titled “Verification steps”- Take the
contentfield value - Normalize it per spec §6:
- Trim leading/trailing whitespace
- Convert to lowercase
- Apply Unicode NFC normalization
- Collapse consecutive whitespace to single spaces
- Compute SHA-256 hex digest of the UTF-8 encoded result
- Compare with the stored
content_hash(format:sha256:<hex>)
Example
Section titled “Example”Content: "User is a cloud infrastructure engineer"
After normalization: "user is a cloud infrastructure engineer"
Expected hash: sha256:e1bae3ec291c99eced01fc91b4152a0cef541fccf2034fc11b3f90f4e4d79b6e
Implementation
Section titled “Implementation”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;}File-Level Integrity Block
Section titled “File-Level Integrity Block”The optional integrity block verifies that the memories array has not been tampered with. The checksum covers only the memories array, not the entire document.
Verification steps
Section titled “Verification steps”Per spec §15:
- Extract the
memoriesarray from the document - Sort the memory objects by
idfield, ascending - Canonicalize the sorted array using RFC 8785 (JSON Canonicalization Scheme)
- Compute SHA-256 of the canonical UTF-8 bytes
- Compare with
integrity.checksum(format:sha256:<hex>) - Verify that
integrity.total_memoriesmatches the length of thememoriesarray
Implementation
Section titled “Implementation”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;}Install the canonicalize package: npm install canonicalize
Cryptographic Signatures
Section titled “Cryptographic Signatures”PAM v1.0 supports optional cryptographic signatures for authenticity verification and tamper detection via the signature block (spec §18).
Signature payload
Section titled “Signature payload”The signature is computed over a specific payload object, not the integrity checksum alone. The payload contains four fields, canonicalized with RFC 8785:
{ "checksum": "<integrity.checksum>", "export_date": "<export_date>", "export_id": "<export_id>", "owner_id": "<owner.id>"}This prevents replay attacks — a valid checksum from one export cannot be reused in a different export with a different ID or date.
Supported algorithms
Section titled “Supported algorithms”| Algorithm | Notes |
|---|---|
Ed25519 | Recommended. Fast, small keys, resistant to side-channel attacks |
ES256 | ECDSA with P-256 curve |
ES384 | ECDSA with P-384 curve |
RS256 | RSA with SHA-256 |
RS384 | RSA with SHA-384 |
RS512 | RSA with SHA-512 |
Verification steps
Section titled “Verification steps”- Extract the
signatureblock - Reconstruct the payload:
{checksum, export_date, export_id, owner_id} - Canonicalize the payload with RFC 8785
- Verify the signature using
signature.algorithm,signature.public_key, andsignature.value(Base64url-encoded per RFC 4648 §5) - If
owner.didis present, optionally resolve the DID document and verify thatsignature.public_keycorresponds to a verification method in the document