AID-H-013.002: White-Box Artifact & Tensor-State Ownership Verification

Version 1.20261015 · Actionable sub-technique

Compare complete, authorized suspect model bytes against a signature-verified release fingerprint by independently recomputing the full artifact-tree digest and canonical safetensors tensor-state digest. This control can establish an exact artifact or canonical tensor-state match; transformed or independently trained artifacts remain a no-exact-match finding rather than proof of unrelated origin.

Pillars: model

Lifecycle: building, validation, operation

Scope boundary

Owns exact authorized suspect-byte comparison by recomputing complete artifact-tree and canonical tensor-state digests against a signature-verified release fingerprint. A no-match result does not prove unrelated lineage after transformation.

AID-H-003.006: AID-H-013.002 independently compares suspect bytes with the release fingerprint; AID-H-003.006 produces and signs the model SBOM and provenance fingerprint manifest. Fingerprint production and independent suspect comparison must remain separate.

AID-H-013.003: AID-H-013.002 performs exact white-box artifact and tensor comparison when complete suspect bytes are available; AID-H-013.003 performs calibrated black-box behavioral challenges when only a compatible endpoint is accessible. White-box exact match is stronger but less broadly applicable than statistical endpoint evidence.

Warning

Exact-Match Evidence, Not General Lineage Proof

Artifact-tree and canonical tensor-state comparison is strong for byte-identical or canonical tensor-identical copies, but expected transformations can erase an exact match. Treat a positive match as bounded technical attribution evidence and a no-exact-match result only as absence of this signal, not proof that the suspect model is unrelated.

Implementation guidance

AID-H-013.002-G001

Compare authorized model artifacts against a signed release fingerprint using both complete artifact-tree and canonical safetensors tensor-state digests.

Use this white-box control only when investigators are authorized to read the complete suspect model artifact and both the reference and suspect use safetensors for all model weights. It consumes a signed fingerprint manifest exported by AID-H-003.006 after release. The consumer verifies the producer identity, schema, reference asset/version, complete file population, artifact-tree digest, canonical tensor-state digest, tokenizer/config digests, signature, and freshness. The upstream PASS is dependency evidence and never becomes this control's PASS.

Runnable signed-manifest loader and canonical file inventory
# File: model_ip/whitebox_fingerprint_verifier.py
from __future__ import annotations

import hashlib
import hmac
import json
import math
import os
import subprocess
import tempfile
from contextlib import ExitStack
from datetime import datetime, timezone
from pathlib import Path

import torch
from safetensors import safe_open

def _required_positive_seconds(name: str) -> float:
    value = float(os.environ[name])
    if not math.isfinite(value) or value <= 0:
        raise RuntimeError(f"{name} must be a finite positive number of seconds")
    return value

MODEL_FINGERPRINT_MANIFEST_VERIFY_TIMEOUT_SECONDS = _required_positive_seconds("VERIFIED_H013_MODEL_FINGERPRINT_MANIFEST_VERIFY_TIMEOUT_SECONDS")



class NotApplicable(RuntimeError):
    pass


class InsufficientData(RuntimeError):
    pass


class ContractViolation(RuntimeError):
    pass


MANIFEST_FIELDS = {
    "schema_version", "manifest_version", "model_id", "release_id",
    "artifact_tree_sha256", "tensor_state_sha256",
    "safetensors_file_count", "tensor_count",
    "tokenizer_sha256", "config_sha256", "not_after",
}
DIGEST_FIELDS = {
    "artifact_tree_sha256", "tensor_state_sha256",
    "tokenizer_sha256", "config_sha256",
}


def canonical_json(value: object) -> bytes:
    return json.dumps(
        value, sort_keys=True, separators=(",", ":"), ensure_ascii=False
    ).encode("utf-8")


def sha256_bytes(value: bytes) -> str:
    return hashlib.sha256(value).hexdigest()


def sha256_file(path: Path) -> str:
    if path.is_symlink():
        raise ContractViolation("fingerprint input is a symbolic link")
    before = path.stat()
    digest = hashlib.sha256()
    with path.open("rb") as handle:
        for block in iter(lambda: handle.read(1024 * 1024), b""):
            digest.update(block)
    after = path.stat()
    identity_before = (
        before.st_dev, before.st_ino, before.st_size, before.st_mtime_ns
    )
    identity_after = (
        after.st_dev, after.st_ino, after.st_size, after.st_mtime_ns
    )
    if identity_before != identity_after:
        raise InsufficientData("fingerprint input changed during hashing")
    return digest.hexdigest()


def verified_manifest(
    path: Path,
    bundle: Path,
    certificate_identity: str,
    oidc_issuer: str,
) -> tuple[dict, str]:
    raw = path.read_bytes()
    bundle_raw = bundle.read_bytes()
    if not raw or not bundle_raw:
        raise InsufficientData("manifest or Sigstore bundle is empty")
    with tempfile.TemporaryDirectory(prefix="model-fingerprint-") as directory:
        root = Path(directory)
        os.chmod(root, 0o700)
        manifest_snapshot = root / "manifest.json"
        bundle_snapshot = root / "bundle.json"
        manifest_snapshot.write_bytes(raw)
        bundle_snapshot.write_bytes(bundle_raw)
        os.chmod(manifest_snapshot, 0o400)
        os.chmod(bundle_snapshot, 0o400)
        subprocess.run([
            "cosign", "verify-blob",
            "--bundle", str(bundle_snapshot),
            "--certificate-identity", certificate_identity,
            "--certificate-oidc-issuer", oidc_issuer,
            str(manifest_snapshot),
        ], check=True, capture_output=True, text=True, timeout=MODEL_FINGERPRINT_MANIFEST_VERIFY_TIMEOUT_SECONDS)
        if not hmac.compare_digest(manifest_snapshot.read_bytes(), raw):
            raise ContractViolation("verified manifest snapshot changed")
    manifest = json.loads(raw.decode("utf-8", errors="strict"))
    if not isinstance(manifest, dict) or set(manifest) != MANIFEST_FIELDS:
        raise ContractViolation("fingerprint manifest schema differs")
    if (
        manifest["schema_version"]
        != "aidefend.model-fingerprint-manifest.v1"
    ):
        raise ContractViolation("unsupported fingerprint manifest")
    for field in {"manifest_version", "model_id", "release_id"}:
        if not isinstance(manifest[field], str) or not manifest[field].strip():
            raise ContractViolation("manifest identity field is empty")
    for field in DIGEST_FIELDS:
        value = manifest[field]
        if (
            not isinstance(value, str)
            or len(value) != 64
            or value != value.lower()
        ):
            raise ContractViolation("manifest digest is not lower-case SHA-256")
        try:
            int(value, 16)
        except ValueError as exc:
            raise ContractViolation("manifest digest is not hexadecimal") from exc
    for field in {"safetensors_file_count", "tensor_count"}:
        if (
            isinstance(manifest[field], bool)
            or not isinstance(manifest[field], int)
            or manifest[field] < 1
        ):
            raise ContractViolation("manifest population count is invalid")
    expires = datetime.fromisoformat(
        str(manifest["not_after"]).replace("Z", "+00:00")
    )
    if expires.tzinfo is None:
        raise ContractViolation("manifest expiry lacks a timezone")
    if expires.astimezone(timezone.utc) <= datetime.now(timezone.utc):
        raise ContractViolation("fingerprint manifest is expired")
    return manifest, sha256_bytes(raw)


def regular_file_population(root: Path) -> list[Path]:
    root = root.resolve(strict=True)
    entries = sorted(root.rglob("*"), key=lambda p: p.relative_to(root).as_posix())
    if any(path.is_symlink() for path in entries):
        raise ContractViolation("model root contains a symbolic link")
    if any(not path.is_dir() and not path.is_file() for path in entries):
        raise ContractViolation("model root contains a non-regular entry")
    files = [path for path in entries if path.is_file()]
    if not files:
        raise InsufficientData("model artifact tree is empty")
    relative = [path.relative_to(root).as_posix() for path in files]
    if len(relative) != len(set(relative)):
        raise ContractViolation("normalized artifact paths are duplicated")
    return files


def artifact_tree(root: Path) -> tuple[str, list[dict]]:
    root = root.resolve(strict=True)
    rows = [
        {
            "path": path.relative_to(root).as_posix(),
            "sha256": sha256_file(path),
            "size_bytes": path.stat().st_size,
        }
        for path in regular_file_population(root)
    ]
    return sha256_bytes(canonical_json(rows)), rows
def tensor_state(root: Path) -> tuple[str, int, int, list[dict]]:
    root = root.resolve(strict=True)
    files = [
        path for path in regular_file_population(root)
        if path.suffix.lower() == ".safetensors"
    ]
    if not files:
        raise NotApplicable("model root has no safetensors weight population")
    locations: dict[str, tuple[Path, object]] = {}
    digest = hashlib.sha256()
    rows: list[dict] = []
    with ExitStack() as stack:
        handles = {
            path: stack.enter_context(
                safe_open(str(path), framework="pt", device="cpu")
            )
            for path in files
        }
        for path, handle in handles.items():
            for name in handle.keys():
                if name in locations:
                    raise ContractViolation(
                        "tensor name is duplicated across safetensors shards"
                    )
                locations[name] = (path, handle)
        if not locations:
            raise InsufficientData("safetensors population contains no tensors")
        for name in sorted(locations):
            path, handle = locations[name]
            tensor = handle.get_tensor(name).detach().cpu().contiguous()
            raw = tensor.view(torch.uint8).numpy().tobytes(order="C")
            metadata = {
                "name": name,
                "dtype": str(tensor.dtype).removeprefix("torch."),
                "shape": list(tensor.shape),
                "byte_length": len(raw),
            }
            metadata_bytes = canonical_json(metadata)
            digest.update(len(metadata_bytes).to_bytes(8, "big"))
            digest.update(metadata_bytes)
            digest.update(len(raw).to_bytes(8, "big"))
            digest.update(raw)
            rows.append({
                **metadata,
                "tensor_sha256": sha256_bytes(raw),
                "source_path": path.relative_to(root).as_posix(),
            })
    return digest.hexdigest(), len(files), len(locations), rows


def fingerprint(root: Path, require_metadata: bool) -> dict:
    root = root.resolve(strict=True)
    tree_sha256, file_rows = artifact_tree(root)
    tensor_sha256, file_count, tensor_count, tensor_rows = tensor_state(root)
    config = root / "config.json"
    tokenizer = root / "tokenizer.json"
    if require_metadata and (not config.is_file() or not tokenizer.is_file()):
        raise ContractViolation(
            "reference root lacks config.json or tokenizer.json"
        )
    return {
        "artifact_tree_sha256": tree_sha256,
        "tensor_state_sha256": tensor_sha256,
        "safetensors_file_count": file_count,
        "tensor_count": tensor_count,
        "tokenizer_sha256": sha256_file(tokenizer) if tokenizer.is_file() else None,
        "config_sha256": sha256_file(config) if config.is_file() else None,
        "file_population": file_rows,
        "tensor_population": tensor_rows,
    }
def compare_artifacts(
    reference_root: Path,
    suspect_root: Path,
    manifest_path: Path,
    manifest_bundle: Path,
    certificate_identity: str,
    oidc_issuer: str,
    expected_model_id: str,
    expected_release_id: str,
    suspect_asset_id: str,
) -> dict:
    if not reference_root.is_dir() or not suspect_root.is_dir():
        raise InsufficientData("reference or suspect artifact root is unavailable")
    if not all(
        isinstance(value, str) and value.strip()
        for value in {
            expected_model_id, expected_release_id, suspect_asset_id
        }
    ):
        raise ContractViolation("case binding is incomplete")
    manifest, manifest_sha256 = verified_manifest(
        manifest_path, manifest_bundle, certificate_identity, oidc_issuer
    )
    if (
        manifest["model_id"] != expected_model_id
        or manifest["release_id"] != expected_release_id
    ):
        raise ContractViolation("manifest is bound to another model release")
    try:
        reference = fingerprint(reference_root, require_metadata=True)
    except NotApplicable as exc:
        raise ContractViolation(
            "signed reference release is outside the supported format"
        ) from exc
    expected = {
        field: manifest[field]
        for field in {
            "artifact_tree_sha256", "tensor_state_sha256",
            "safetensors_file_count", "tensor_count",
            "tokenizer_sha256", "config_sha256",
        }
    }
    actual = {field: reference[field] for field in expected}
    if not hmac.compare_digest(
        canonical_json(actual), canonical_json(expected)
    ):
        raise ContractViolation(
            "reference bytes do not reproduce the signed fingerprint"
        )
    suspect = fingerprint(suspect_root, require_metadata=False)
    if (
        suspect["artifact_tree_sha256"]
        == manifest["artifact_tree_sha256"]
    ):
        finding = "EXACT_ARTIFACT_MATCH"
    elif (
        suspect["tensor_state_sha256"]
        == manifest["tensor_state_sha256"]
    ):
        finding = "CANONICAL_TENSOR_MATCH"
    else:
        finding = "NO_EXACT_MATCH"
    return {
        "schema_version": "aidefend.whitebox-ownership-evidence.v1",
        "status": "PASS",
        "finding": finding,
        "model_id": manifest["model_id"],
        "release_id": manifest["release_id"],
        "suspect_asset_id": suspect_asset_id,
        "manifest_sha256": manifest_sha256,
        "manifest_bundle_sha256": sha256_file(manifest_bundle),
        "certificate_identity": certificate_identity,
        "oidc_issuer": oidc_issuer,
        "reference": reference,
        "suspect": suspect,
    }


def run_with_status(**kwargs) -> dict:
    try:
        return compare_artifacts(**kwargs)
    except NotApplicable as exc:
        return {"status": "NOT_APPLICABLE", "reason": str(exc)}
    except ContractViolation as exc:
        return {"status": "FAIL", "reason": str(exc)}
    except (InsufficientData, PermissionError) as exc:
        return {"status": "INSUFFICIENT_DATA", "reason": str(exc)}
    except Exception as exc:
        return {"status": "ERROR", "reason": type(exc).__name__}


def run_and_write(evidence_path: Path, **kwargs) -> dict:
    result = run_with_status(**kwargs)
    payload = canonical_json(result) + b"\n"
    evidence_path.parent.mkdir(parents=True, exist_ok=True)
    if evidence_path.exists():
        raise FileExistsError("refusing to overwrite ownership evidence")
    descriptor, temporary_name = tempfile.mkstemp(
        prefix=evidence_path.name + ".", dir=evidence_path.parent
    )
    temporary_path = Path(temporary_name)
    try:
        with os.fdopen(descriptor, "wb") as handle:
            handle.write(payload)
            handle.flush()
            os.fsync(handle.fileno())
        os.chmod(temporary_path, 0o400)
        os.link(temporary_path, evidence_path)
    finally:
        temporary_path.unlink(missing_ok=True)
    return result
Execution, independent replay, evidence, and outcomes

Run the verifier inside a network-denied, read-only investigation worker against immutable snapshots of both roots. The certificate identity and issuer must come from the verifier image's signed deployment policy, not case-supplied values. Write the canonical result, sign it with an external evidence identity, and store it with the case manifest in append-only storage:

python -m model_ip.run_whitebox_case   --reference-root /evidence/reference   --suspect-root /evidence/suspect   --manifest /policy/model-fingerprint.json   --manifest-bundle /policy/model-fingerprint.sigstore.json   --evidence /out/whitebox-ownership.json

cosign sign-blob --yes   --bundle /out/whitebox-ownership.sigstore.json   /out/whitebox-ownership.json

A second read-only runner must verify both Sigstore bundles, re-enumerate every regular file, recompute every file and tensor digest with the same pinned Python, PyTorch, and safetensors versions, and compare its canonical result digest with the first runner. Retain the signed manifest, both bundles, model/release/case IDs, immutable worker-image and dependency-lock digests, complete reference and suspect file/tensor populations, all aggregate and per-object digests, result finding, writer receipt, and independent replay receipt. Restrict these records because filenames and tensor metadata may expose model architecture.

Action: Reproduce the signed reference fingerprint, compare the entire suspect artifact and canonical tensor populations, preserve the bounded finding as signed evidence, and require an independent byte-level replay before using it in an ownership investigation.

Open-source 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-013.002: White-Box Artifact & Tensor-State Ownership Verification | AIDEFEND

Loading interactive view…

A Practical Knowledge Base for AI Security Defenses

Version