AID-H-005.002: Homomorphic Encryption for AI

Version 1.20261015 · Actionable sub-technique

Apply homomorphic encryption so supported AI computations operate on ciphertext and plaintext is not exposed to the untrusted compute path. Pure HE workflows keep protected values encrypted throughout the evaluated computation; hybrid HE-plus-TEE workflows may decrypt only inside measured enclave memory for operations that the HE circuit cannot support, then re-encrypt before data leaves the attested boundary. Applicability depends on a model and operator set that can be represented within the selected scheme, precision, multiplicative-depth, key-management, and latency constraints.

Pillars: data, model, infra

Lifecycle: building, operation

Scope boundary

Owns privacy-preserving supported computation over ciphertext, including scheme, precision, multiplicative depth, key management, operator coverage, and explicit hybrid HE-plus-attested-enclave transitions.

AID-H-009.005: AID-H-005.002 protects data by evaluating supported operations over ciphertext and may use an enclave only for unsupported operations; AID-H-009.005 protects plaintext computation inside a remotely attested confidential runtime. Homomorphic encryption and trusted-execution confidentiality have different plaintext exposure, operator, and trust assumptions.

Warning

Extremely High on Inference Latency & Computational Cost

Performing computations on encrypted data is orders of magnitude slower than on plaintext.

Inference Latency: Can be 100x to 10,000x higher than a non-encrypted model. A prediction that takes milliseconds on plaintext could take many seconds or even minutes.

Training: Training a model with HE is often computationally prohibitive for all but the simplest models.

Implementation guidance

AID-H-005.002-G001

Implement and tune a homomorphic encryption configuration for the target AI workload, including scheme choice, depth budgeting, and parameter sizing.
Before you begin

General-purpose deep models with unsupported depth/precision remain unapproved, not silently protected.

Production implementation

Compile the exact circuit, obtain parameters from the maintained library's security estimator or reviewed parameter set rather than hand-built literals, generate keys in the approved HSM/threshold service, distribute evaluation keys without secret keys, validate ciphertext context/level/size before computation, enforce depth and noise/precision bounds at every stage, return ciphertext only, and reject context mismatch, exhausted depth or accuracy/SLO breach.

Independent verification

An independent cryptography runner rebuilds the context from the signed profile, validates provider security estimates, executes known-answer, malformed-ciphertext, wrong-context/key, depth-boundary, precision/overflow and load fixtures, compares encrypted/decrypted results with the plaintext reference within policy tolerance, and reconciles every eligible operation.

OpenFHE CKKS executable fixture
# File: he/ckks_linear_fixture.py
# Dependency: openfhe Python wrapper, version pinned in the build lock
from openfhe import (
    CCParamsCKKSRNS, GenCryptoContext, KEYSWITCH, LEVELEDSHE, PKE,
)


def encrypted_affine(
    values: list[float],
    weights: list[float],
    bias: list[float],
    *,
    multiplicative_depth: int,
    scaling_modulus_bits: int,
):
    if not values or len(values) != len(weights) or len(values) != len(bias):
        raise ValueError("vector populations differ")
    if multiplicative_depth < 1 or scaling_modulus_bits < 1:
        raise ValueError("invalid signed CKKS parameters")
    parameters = CCParamsCKKSRNS()
    parameters.SetMultiplicativeDepth(multiplicative_depth)
    parameters.SetScalingModSize(scaling_modulus_bits)
    parameters.SetBatchSize(len(values))
    context = GenCryptoContext(parameters)
    context.Enable(PKE)
    context.Enable(KEYSWITCH)
    context.Enable(LEVELEDSHE)
    keys = context.KeyGen()
    context.EvalMultKeyGen(keys.secretKey)

    encrypted = context.Encrypt(
        keys.publicKey, context.MakeCKKSPackedPlaintext(values)
    )
    weighted = context.EvalMult(
        encrypted, context.MakeCKKSPackedPlaintext(weights)
    )
    result = context.EvalAdd(
        weighted, context.MakeCKKSPackedPlaintext(bias)
    )
    # Fixture-only readback. Production keeps the secret key in a separate
    # threshold or attested decryptor and returns only the authorized result.
    plaintext = context.Decrypt(keys.secretKey, result)
    plaintext.SetLength(len(values))
    return plaintext.GetRealPackedValue()

Derive depth, scale, ring dimension, security level, batch size, and acceptable approximation error from the reviewed circuit and signed cryptographic profile. Reject a circuit whose measured depth or precision exceeds that profile; never copy these fixture parameters into production without that analysis.

AID-H-005.002-G002

Use partially homomorphic encryption for specific operations (addition, multiplication).
Before you begin

Owned by cryptography and aggregation-service teams for bounded additive or multiplicative workloads that need only a reviewed partially homomorphic operation, such as privacy-preserving sums, and do not require an FHE circuit.

Production implementation

Generate keys under threshold/separated custody, publish only the scoped public key, authenticate every participant ciphertext and enforce domain/range proofs or trusted-client bounds, reject duplicate/wrong-key/malformed inputs, aggregate only the complete authorized cohort, release only the policy-approved aggregate to the threshold decryptor, enforce minimum cohort and purpose, and never decrypt or log individual contributions.

Independent verification

An independent runner verifies the key ceremony and threshold, executes known-answer, negative/overflow, malformed, duplicate, missing-participant, wrong-key and below-minimum-cohort fixtures, compares the decrypted aggregate with an authorized plaintext reference, and reconciles every eligible participant/input.

Executable additive-homomorphic path
# File: he/paillier_sum.py
# Dependency: phe
import os

from phe import paillier


def encrypt_vector(public_key, values: list[float]):
    if not values:
        raise ValueError("empty vector")
    return [public_key.encrypt(float(value)) for value in values]


def aggregate(ciphertext_vectors: list[list]):
    if not ciphertext_vectors:
        raise ValueError("empty ciphertext cohort")
    width = len(ciphertext_vectors[0])
    if width == 0 or any(len(vector) != width for vector in ciphertext_vectors):
        raise ValueError("ciphertext vector shapes differ")
    return [
        sum((vector[index] for vector in ciphertext_vectors[1:]),
            ciphertext_vectors[0][index])
        for index in range(width)
    ]


# Integration fixture; production provisions the private key only to the
# separated aggregate-decryption service.
policy_key_bits = int(os.environ["HE_PAILLIER_KEY_BITS"])
if policy_key_bits < 1:
    raise ValueError("signed HE key-size policy is invalid")
public_key, private_key = paillier.generate_paillier_keypair(
    n_length=policy_key_bits
)
encrypted = [encrypt_vector(public_key, [1.0, 2.0]),
             encrypt_vector(public_key, [3.0, 4.0])]
decrypted_sum = [private_key.decrypt(value) for value in aggregate(encrypted)]
assert decrypted_sum == [4.0, 6.0]

Authenticate every ciphertext and bind it to one cohort, vector schema, key ID, and round. The aggregator must not receive the private key; the separated decryptor verifies the complete cohort and releases only the policy-authorized aggregate.

AID-H-005.002-G003

Implement hybrid approaches combining HE with secure enclaves for complex operations.
Bind deployment values to one signed runtime policy

Use this hybrid path only for a reviewed circuit whose non-linear stage must enter a measured confidential-computing boundary. The separately administered runtime policy supplies the digest-pinned image, namespace, Pod identity, runtime class, scratch-memory quantity, command timeout, and digest of the Rego attestation rule. A protected bootstrap profile supplies only the finite timeout needed to verify that signed policy.

#!/usr/bin/env bash
set -euo pipefail
umask 077
test -n "$AIDEFEND_SIGNATURE_VERIFY_TIMEOUT_SECONDS"
[[ "$AIDEFEND_SIGNATURE_VERIFY_TIMEOUT_SECONDS" =~ ^[1-9][0-9]*$ ]]
SNAPSHOT_DIR=$(mktemp -d)
chmod 0700 "$SNAPSHOT_DIR"
trap 'rm -rf -- "$SNAPSHOT_DIR"' EXIT

install -m 0400 -- policy/he-stage-runtime.json "$SNAPSHOT_DIR/runtime-policy.json"
install -m 0400 -- policy/he-stage-runtime.sig "$SNAPSHOT_DIR/runtime-policy.sig"
install -m 0400 -- /opt/aidefend/trust/he-stage-runtime.pub "$SNAPSHOT_DIR/runtime-policy.pub"
install -m 0400 -- evidence/he-stage-attestation.json "$SNAPSHOT_DIR/attestation.json"
install -m 0400 -- evidence/he-stage-attestation.sig "$SNAPSHOT_DIR/attestation.sig"
install -m 0400 -- /opt/aidefend/trust/attestation-authority.pub "$SNAPSHOT_DIR/attestation.pub"
install -m 0400 -- policy/he-stage.rego "$SNAPSHOT_DIR/he-stage.rego"
install -m 0400 -- /opt/aidefend/trust/he-evaluator-image.pub "$SNAPSHOT_DIR/image.pub"

timeout --signal=KILL "$AIDEFEND_SIGNATURE_VERIFY_TIMEOUT_SECONDS" cosign verify-blob \
  --key "$SNAPSHOT_DIR/runtime-policy.pub" --bundle "$SNAPSHOT_DIR/runtime-policy.sig" \
  "$SNAPSHOT_DIR/runtime-policy.json"

python - "$SNAPSHOT_DIR/runtime-policy.json" "$SNAPSHOT_DIR/attestation.json" \
  "$SNAPSHOT_DIR/he-stage.rego" "$SNAPSHOT_DIR/pod.json" <<'PY'
import hashlib, json, os, re, sys
from pathlib import Path
FIELDS={"schema_version","policy_version","namespace","pod_name","runtime_class_name","image_ref","scratch_size_limit","command_timeout_seconds","rego_sha256"}
DNS=re.compile(r"^[a-z0-9](?:[-a-z0-9.]*[a-z0-9])?$")
IMAGE=re.compile(r"^[^\s]+@sha256:[0-9a-f]{64}$")
QUANTITY=re.compile(r"^[1-9][0-9]*(?:Ki|Mi|Gi|Ti|Pi|Ei|K|M|G|T|P|E)$")
SHA256=re.compile(r"^[0-9a-f]{64}$")
def strict(path,label):
    raw=Path(path).read_bytes()
    def unique(pairs):
        result={}
        for key,value in pairs:
            if key in result: raise ValueError(f"duplicate {label} key: {key}")
            result[key]=value
        return result
    def reject(value): raise ValueError(f"non-finite {label} value: {value}")
    value=json.loads(raw.decode("utf-8",errors="strict"),object_pairs_hook=unique,parse_constant=reject)
    if not isinstance(value,dict): raise ValueError(label+" must be an object")
    return value,raw
policy,policy_raw=strict(sys.argv[1],"runtime policy")
attestation,_=strict(sys.argv[2],"attestation")
if set(policy)!=FIELDS or policy["schema_version"]!="aidefend.hybrid-he-stage-policy.v1": raise ValueError("runtime policy schema differs")
for field in FIELDS-{"command_timeout_seconds"}:
    if not isinstance(policy[field],str) or not policy[field]: raise ValueError(field+" must be non-empty")
if not all(DNS.fullmatch(policy[field]) for field in ("namespace","pod_name","runtime_class_name")): raise ValueError("Kubernetes identity invalid")
if not IMAGE.fullmatch(policy["image_ref"]): raise ValueError("image_ref must be digest pinned")
if not QUANTITY.fullmatch(policy["scratch_size_limit"]): raise ValueError("invalid scratch_size_limit")
timeout_seconds=policy["command_timeout_seconds"]
if isinstance(timeout_seconds,bool) or not isinstance(timeout_seconds,int) or timeout_seconds<=0: raise ValueError("command timeout must be positive")
if not SHA256.fullmatch(policy["rego_sha256"]): raise ValueError("invalid Rego digest")
if hashlib.sha256(Path(sys.argv[3]).read_bytes()).hexdigest()!=policy["rego_sha256"]: raise PermissionError("Rego differs from signed policy")
if not attestation: raise ValueError("attestation object is empty")
pod={"apiVersion":"v1","kind":"Pod","metadata":{"name":policy["pod_name"],"namespace":policy["namespace"],"labels":{"app":policy["pod_name"]}},"spec":{"runtimeClassName":policy["runtime_class_name"],"automountServiceAccountToken":False,"restartPolicy":"Never","containers":[{"name":"evaluator","image":policy["image_ref"],"args":["--require-attestation","--ciphertext-input-only"],"securityContext":{"allowPrivilegeEscalation":False,"readOnlyRootFilesystem":True,"runAsNonRoot":True,"capabilities":{"drop":["ALL"]}},"volumeMounts":[{"name":"scratch","mountPath":"/scratch"}]}],"volumes":[{"name":"scratch","emptyDir":{"medium":"Memory","sizeLimit":policy["scratch_size_limit"]}}]}}
payload=(json.dumps(pod,sort_keys=True,separators=(",",":"))+"\n").encode()
fd=os.open(sys.argv[4],os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o400)
with os.fdopen(fd,"wb") as handle: handle.write(payload); handle.flush(); os.fsync(handle.fileno())
print(json.dumps({"policy_sha256":hashlib.sha256(policy_raw).hexdigest(),"manifest_sha256":hashlib.sha256(payload).hexdigest()},sort_keys=True))
PY

COMMAND_TIMEOUT_SECONDS=$(jq -er '.command_timeout_seconds' "$SNAPSHOT_DIR/runtime-policy.json")
IMAGE_REF=$(jq -er '.image_ref' "$SNAPSHOT_DIR/runtime-policy.json")
NAMESPACE=$(jq -er '.namespace' "$SNAPSHOT_DIR/runtime-policy.json")
POD_NAME=$(jq -er '.pod_name' "$SNAPSHOT_DIR/runtime-policy.json")
timeout --signal=KILL "$COMMAND_TIMEOUT_SECONDS" cosign verify --key "$SNAPSHOT_DIR/image.pub" "$IMAGE_REF"
timeout --signal=KILL "$COMMAND_TIMEOUT_SECONDS" cosign verify-blob \
  --key "$SNAPSHOT_DIR/attestation.pub" --bundle "$SNAPSHOT_DIR/attestation.sig" \
  "$SNAPSHOT_DIR/attestation.json"
OPA_RESULT=$(timeout --signal=KILL "$COMMAND_TIMEOUT_SECONDS" opa eval --fail --format raw \
  --data "$SNAPSHOT_DIR/he-stage.rego" --input "$SNAPSHOT_DIR/attestation.json" \
  'data.aidefend.he_stage.allow == true')
test "$OPA_RESULT" = true
timeout --signal=KILL "$COMMAND_TIMEOUT_SECONDS" kubectl apply --server-side --dry-run=server -f "$SNAPSHOT_DIR/pod.json"
timeout --signal=KILL "$COMMAND_TIMEOUT_SECONDS" kubectl apply --server-side --field-manager=aidefend-hybrid-stage -f "$SNAPSHOT_DIR/pod.json"
timeout --signal=KILL "$COMMAND_TIMEOUT_SECONDS" kubectl wait --namespace "$NAMESPACE" \
  --for=condition=Ready --timeout="$COMMAND_TIMEOUT_SECONDS"s "pod/$POD_NAME"
timeout --signal=KILL "$COMMAND_TIMEOUT_SECONDS" kubectl get pod --namespace "$NAMESPACE" \
  "$POD_NAME" -o json > "$SNAPSHOT_DIR/pod-readback.json"

python - "$SNAPSHOT_DIR/pod.json" "$SNAPSHOT_DIR/pod-readback.json" <<'PY'
import json,sys
from pathlib import Path
expected=json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
observed=json.loads(Path(sys.argv[2]).read_text(encoding="utf-8"))
if not observed.get("metadata",{}).get("uid"): raise PermissionError("pod UID readback missing")
for field in ("runtimeClassName","automountServiceAccountToken"):
    if observed.get("spec",{}).get(field)!=expected["spec"][field]: raise PermissionError("pod readback mismatch: "+field)
want=expected["spec"]["containers"][0]; got=observed.get("spec",{}).get("containers",[{}])[0]
for field in ("name","image","args","securityContext","volumeMounts"):
    if got.get(field)!=want[field]: raise PermissionError("container readback mismatch: "+field)
if observed.get("spec",{}).get("volumes")!=expected["spec"]["volumes"]: raise PermissionError("scratch volume readback mismatch")
PY

for fixture in wrong-measurement wrong-image stale wrong-nonce wrong-audience; do
  set +e
  timeout --signal=KILL "$COMMAND_TIMEOUT_SECONDS" opa eval --fail --format raw \
    --data "$SNAPSHOT_DIR/he-stage.rego" --input "evidence/he-stage-fixtures/$fixture.json" \
    'data.aidefend.he_stage.allow == true' >/dev/null
  rc=$?
  set -e
  test "$rc" -eq 1
done
Evidence and failure handling

Retain the exact private policy, signature, trust-key, attestation, Rego, manifest, and Pod-readback snapshots plus image-verification output and negative-fixture results. Deny deployment or key release on signature failure, malformed or duplicate JSON, a mutable image reference, policy/Rego mismatch, attestation denial or verifier error, Kubernetes dry-run/apply/readback mismatch, timeout, or any negative fixture that is accepted. Host processes never receive plaintext or scoped decryption material.

Open-source tools

Source-available / open-weight tools

Commercial / hosted tools

Threat mappings

MITRE ATLAS

MAESTRO

OWASP LLM Top 10 2026

OWASP ML Top 10 2023

OWASP Top 10 for Agentic Applications 2026

NIST Adversarial Machine Learning 2025

Cisco Integrated AI Security and Safety Framework

Google Secure AI Framework 2.0 - Risks

Databricks AI Security Framework 3.0

AID-H-005.002: Homomorphic Encryption for AI | AIDEFEND

Loading interactive view…

A Practical Knowledge Base for AI Security Defenses

Version