Skip to content
8 changes: 8 additions & 0 deletions src/agentrust_trace/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from __future__ import annotations


from typing import Annotated, Literal

from pydantic import BaseModel, ConfigDict, Field, model_validator

_DIGEST_RE = r"^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$"
_JWK_PRIVATE_PARAMS = frozenset({"d", "p", "q", "dp", "dq", "qi", "k"})

DigestStr = Annotated[str, Field(pattern=_DIGEST_RE)]

Expand Down Expand Up @@ -97,6 +99,12 @@ def _require_key_material(self) -> JWK:
raise ValueError(
f"jwk with kty={self.kty!r} must carry key material: missing {', '.join(missing)}"
)
extra = dict(self.model_extra) if self.model_extra else {}
private = _JWK_PRIVATE_PARAMS & extra.keys()
if private:
raise ValueError(
f"cnf.jwk must not contain private key parameters: {sorted(private)}"
)
return self


Expand Down
7 changes: 7 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,10 @@ def test_okp_jwk_with_key_material_accepted() -> None:
}
record = TrustRecord.model_validate(data)
assert record.cnf.jwk.x is not None

def test_jwk_private_key_d_rejected() -> None:
"""cnf.jwk must not contain private key parameter d (RFC 8747 §3)."""
data = _load("intel-tdx.json")
data["cnf"]["jwk"]["d"] = "PRIVATE_KEY_MATERIAL"
with pytest.raises(ValidationError):
TrustRecord.model_validate(data)
Loading