Skip to content

Encrypted surrogate mappings

Reversible redaction mappings are re-identification material. Treat them as secrets and keep them separate from the de-identified text. OpenMed provides a small local storage boundary in openmed.core.surrogate_vault_crypto.SurrogateVaultCrypto for applications that need to retain a mapping.

Key handling

The caller must provide exactly 32 bytes of key material. The helper does not generate, persist, print, or recover a key. Generate and load that material from the application's approved secret-management path, for example:

import os

from openmed.core.surrogate_vault_crypto import SurrogateVaultCrypto

key = bytes.fromhex(os.environ["OPENMED_SURROGATE_VAULT_KEY_HEX"])
vault = SurrogateVaultCrypto(key)

The key is never written into the encrypted envelope. Do not put it in source control, a report, a log message, a filename, or the same location as the encrypted mapping. A missing, non-bytes, or incorrectly sized key fails before any mapping operation.

The implementation uses deterministic AES-256-SIV authenticated encryption. Canonical serialization makes equivalent mappings produce the same envelope, which is useful for local reproducibility. Determinism reveals only whether two complete encrypted mappings are equal; it does not reveal mapping keys or values. Use a fresh key when this equality signal is not acceptable.

Install the optional cryptography dependency before using this feature:

pip install "openmed[integrity]"

Encrypt and restore a mapping

The mapping remains in process memory while it is encrypted. Only the JSON envelope is written to disk, and file writes use a temporary file followed by an atomic replacement. The temporary file is owner-only on POSIX; on Windows, it inherits access control from the destination directory, which the caller must restrict appropriately:

from pathlib import Path

mapping = {
    "<SURROGATE_A>": "<SYNTHETIC_SOURCE_A>",
    "<SURROGATE_B>": "<SYNTHETIC_SOURCE_B>",
}

path = Path("private/surrogate-mapping.json")
vault.write(path, mapping)
restored = vault.read(path)
assert restored == mapping

For one-shot operations, use save_mapping(path, mapping, key) and load_mapping(path, key). The serialized envelope contains a schema version, the algorithm name, and base64 ciphertext. It contains no plaintext mapping values, source text, key material, or network metadata.

Operational boundaries

  • Keep the encrypted mapping in a separate access-controlled location from redacted output.
  • Do not log the mapping, key, encrypted payload, or exception objects from an untrusted serialization boundary.
  • Re-identification remains an application authorization decision; encryption does not provide compliance certification or make a reversible workflow anonymous.
  • The helper performs no network access and has no telemetry or remote key lookup. Missing or tampered files fail closed with high-level errors that do not include mapping contents.