Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to Agent Manifest are documented here. Format follows [Keep

## [Unreleased]

## [0.10.0] — 2026-08-01

Exports the SEV-SNP ABI offset table so downstreams can delete the ctypes mirrors they kept solely to read offsets from. Completes what 0.9.0 started: cmcp used its struct as an offset oracle across seven test files, so sharing the parse without sharing the table would only have moved the duplication into test scaffolding, where it would drift silently.

### Added

**[SDK]** **`SNP_OFFSETS` and `SNP_REPORT_LEN` are public.** The offsets are the contract consumers build and appraise reports against, and they are now checked against the genuine Azure capture rather than against themselves.

## [0.9.0] — 2026-08-01

Shares the SEV-SNP report union so cmcp and ca2a can delete four copies of the layout between them, and restores a check that existed only in the copies being deleted: the report's declared `sig_algo` is now verified before the signature is checked under it. Phase A2 of consolidating TEE verification into this package.
Expand Down
2 changes: 1 addition & 1 deletion python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "agent-manifest"
version = "0.9.0"
version = "0.10.0"
description = "Agent Manifest SDK — cryptographically anchor all 10 artifacts defining an AI agent at deployment"
readme = "README.md"
requires-python = ">=3.11"
Expand Down
4 changes: 2 additions & 2 deletions python/src/agent_manifest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
verify_attestation_chain, ChainVerificationResult, SignatureStatus,
)
from ._snp_verify import (
SIG_ALGO_ECDSA_P384_SHA384,
SIG_ALGO_ECDSA_P384_SHA384, SNP_OFFSETS, SNP_REPORT_LEN,
SnpReport, SnpVerificationError,
parse_snp_report, parse_hcl_report, load_snp_cert_chain,
verify_snp_signature, verify_vcek_chain, verify_runtime_data_binding,
Expand Down Expand Up @@ -90,7 +90,7 @@
"AttestationReport", "AttestationUnavailableError", "RuntimeAttestationReport",
"TPMProvider", "AzureCVMProvider", "SEVSNPProvider", "TDXProvider", "OPAQUEProvider",
"verify_attestation_chain", "ChainVerificationResult", "SignatureStatus",
"SIG_ALGO_ECDSA_P384_SHA384",
"SIG_ALGO_ECDSA_P384_SHA384", "SNP_OFFSETS", "SNP_REPORT_LEN",
"SnpReport", "SnpVerificationError",
"parse_snp_report", "parse_hcl_report", "load_snp_cert_chain",
"verify_snp_signature", "verify_vcek_chain", "verify_runtime_data_binding",
Expand Down
19 changes: 19 additions & 0 deletions python/src/agent_manifest/_snp_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,25 @@
# distinguished, which is exactly why it must be checked rather than assumed.
SIG_ALGO_ECDSA_P384_SHA384 = 1

# The ABI offsets, public because they are the contract consumers build and
# appraise reports against. Downstreams previously kept their own ctypes mirror
# of this layout purely to read offsets off it; four copies of one table is four
# chances for them to disagree, so the table is exported instead.
SNP_REPORT_LEN = _SNP_REPORT_LEN
SNP_OFFSETS: dict[str, int] = {
"version": _OFF_VERSION,
"guest_svn": _OFF_GUEST_SVN,
"policy": _OFF_POLICY,
"vmpl": _OFF_VMPL,
"sig_algo": _OFF_SIG_ALGO,
"report_data": _OFF_REPORT_DATA,
"measurement": _OFF_MEASUREMENT,
"host_data": _OFF_HOST_DATA,
"reported_tcb": _OFF_REPORTED_TCB,
"chip_id": _OFF_CHIP_ID,
"signature": _OFF_SIGNATURE,
}

_HCL_MAGIC = b"HCLA"
_HCL_SNP_REPORT_OFFSET = 0x20

Expand Down
15 changes: 15 additions & 0 deletions python/tests/test_snp_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,3 +346,18 @@ def test_load_snp_cert_chain_rejects_unparseable_input():

with pytest.raises(SnpVerificationError, match="could not parse"):
load_snp_cert_chain(b"-----BEGIN CERTIFICATE-----\nnot a cert\n-----END CERTIFICATE-----\n")


def test_public_offsets_match_the_genuine_capture():
"""The exported table is the contract downstreams build reports against, so
it is checked against real silicon rather than against itself."""
from agent_manifest import SNP_OFFSETS, SNP_REPORT_LEN

raw = SNP.read_bytes()
rep = parse_snp_report(raw)

assert SNP_REPORT_LEN == len(rep.raw) == 0x4A0
assert raw[SNP_OFFSETS["measurement"]:SNP_OFFSETS["measurement"] + 48] == rep.measurement
assert raw[SNP_OFFSETS["report_data"]:SNP_OFFSETS["report_data"] + 64] == rep.report_data
assert raw[SNP_OFFSETS["chip_id"]:SNP_OFFSETS["chip_id"] + 64] == rep.chip_id
assert int.from_bytes(raw[SNP_OFFSETS["sig_algo"]:SNP_OFFSETS["sig_algo"] + 4], "little") == 1
Loading