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: 6 additions & 2 deletions ct.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4600,6 +4600,9 @@ variants = [
"Aes128GcmSiv",
"Aes256GcmSiv",
"AesSivCmac256",
"Aes128HeaderProtection",
"Aes256HeaderProtection",
"ChaCha20HeaderProtection",
"ChaCha20Poly1305",
"XChaCha20Poly1305",
"Aegis256",
Expand All @@ -4622,9 +4625,10 @@ entrypoints = [
"ChaCha20 keystream helpers",
"Poly1305 block helpers",
"AES-SIV-CMAC-256 private CMAC/S2V and CTR helpers",
"typed header-protection constructors and mask generation",
]
secrets = ["key", "plaintext", "opened_plaintext", "poly_key", "hash_subkey"]
public = ["nonce", "aad", "input_length", "backend_caps"]
secrets = ["key", "plaintext", "opened_plaintext", "poly_key", "hash_subkey", "full cipher output block"]
public = ["nonce", "aad", "input_length", "header-protection sample", "backend_caps"]
may_leak = ["input_length", "public_backend_dispatch"]
must_not_leak_ref = "common_secret_data"
required = ["tier_b"]
Expand Down
14 changes: 14 additions & 0 deletions scripts/ct/evidence_validation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,20 @@ def public_operand_primitive_without_root(manifest) -> None:
in manifest_errors(public_operand_primitive_without_root)
)

def evidence_unit_with_undeclared_variant(manifest) -> None:
unit = next(
row
for row in manifest["evidence_unit"]
if row["id"] == "aead.symmetric_transform.aes128_header_protection"
)
unit["variant"] = "UndeclaredVariant"

assert (
"evidence unit aead.symmetric_transform.aes128_header_protection variant 'UndeclaredVariant' "
"is not declared by primitive aead.symmetric_transform"
in manifest_errors(evidence_unit_with_undeclared_variant)
)

with tempfile.TemporaryDirectory() as temporary:
temporary_path = Path(temporary)
hashes = temporary_path / "hashes.txt"
Expand Down
28 changes: 20 additions & 8 deletions scripts/ct/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,10 +465,13 @@ def validate_manifest(root: Path, selected_target: str, errors: list[str], warni
fail(errors, "CT harness equality retention list does not exactly match ct.toml")

primitive_ids: set[str] = set()
primitive_by_id: dict[str, dict] = {}
for primitive in ct.get("primitive", []):
primitive_id = primitive.get("id", "<unnamed>")
if primitive_id in primitive_ids:
fail(errors, f"duplicate primitive id {primitive_id}")
else:
primitive_by_id[primitive_id] = primitive
primitive_ids.add(primitive_id)

claim = primitive.get("claim")
Expand All @@ -477,6 +480,14 @@ def validate_manifest(root: Path, selected_target: str, errors: list[str], warni
if claim == "ct-claimed":
fail(errors, f"primitive {primitive_id} is ct-claimed before release evidence gates exist")

variants = primitive.get("variants", [])
if (
not isinstance(variants, list)
or any(not isinstance(variant, str) or not variant for variant in variants)
or len(variants) != len(set(variants))
):
fail(errors, f"primitive {primitive_id} variants must be a list of unique non-empty strings")

unsupported_timing_targets = primitive.get("physical_timing_unsupported_targets", [])
if unsupported_timing_targets and (
not isinstance(unsupported_timing_targets, list)
Expand Down Expand Up @@ -624,11 +635,15 @@ def validate_manifest(root: Path, selected_target: str, errors: list[str], warni
fail(errors, f"duplicate evidence unit id {unit_id}")
unit_ids.add(unit_id)

primitive = unit.get("primitive")
if primitive not in primitive_ids:
fail(errors, f"evidence unit {unit_id} references unknown primitive {primitive!r}")
if not unit.get("variant"):
primitive_id = unit.get("primitive")
primitive = primitive_by_id.get(primitive_id)
if primitive is None:
fail(errors, f"evidence unit {unit_id} references unknown primitive {primitive_id!r}")
variant = unit.get("variant")
if not variant:
fail(errors, f"evidence unit {unit_id} missing variant")
elif primitive is not None and variant not in primitive.get("variants", []):
fail(errors, f"evidence unit {unit_id} variant {variant!r} is not declared by primitive {primitive_id}")

dudect = unit.get("dudect", [])
if not isinstance(dudect, list):
Expand All @@ -639,7 +654,7 @@ def validate_manifest(root: Path, selected_target: str, errors: list[str], warni
if case is None:
fail(errors, f"evidence unit {unit_id} references unknown DudeCT case {case_name!r}")
continue
if case.get("primitive") != primitive:
if case.get("primitive") != primitive_id:
fail(errors, f"evidence unit {unit_id} references DudeCT case {case_name} for different primitive {case.get('primitive')!r}")
if case.get("gate") == "diagnostic":
fail(errors, f"evidence unit {unit_id} cannot rely on diagnostic DudeCT case {case_name}")
Expand Down Expand Up @@ -945,11 +960,8 @@ def validate_strict_coverage(ct: dict, errors: list[str], target: str | None = N
units = evidence_units_by_primitive.get(primitive_id, [])
covered_variants = {unit.get("variant") for unit in units if unit.get("dudect")}
missing_variants = sorted(set(variants) - covered_variants)
extra_variants = sorted(covered_variants - set(variants))
if missing_variants:
fail(errors, f"primitive {primitive_id} requires DudeCT evidence for variant(s): {', '.join(missing_variants)}")
if extra_variants:
fail(errors, f"primitive {primitive_id} has evidence unit(s) for unknown variant(s): {', '.join(extra_variants)}")
for unit in units:
unit_id = unit.get("id", "<unnamed>")
for case_name in unit.get("dudect", []):
Expand Down