Skip to content
Open
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 xtest/sdk/go/cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ if [ "$1" == "supports" ]; then
"${cmd[@]}" --version --json | jq -re .sdk_version | awk -F. '{ if ($1 > 0 || ($1 == 0 && $2 > 3) || ($1 == 0 && $2 == 3 && $3 >= 18)) exit 0; else exit 1; }'
exit $?
;;
assertion_schema_v2)
# V2 assertion schema (urn:opentdf:system:metadata:v2) support
# Uses root signature for binding instead of aggregate hash
# Introduced in SDK version 0.10.0
set -o pipefail
"${cmd[@]}" --version --json | jq -re .sdk_version | awk -F. '{ if ($1 > 0 || ($1 == 0 && $2 >= 10)) exit 0; else exit 1; }'
exit $?
;;
*)
echo "Unknown feature: $2"
exit 2
Expand Down
89 changes: 89 additions & 0 deletions xtest/tdfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
feature_type = Literal[
"assertions",
"assertion_verification",
# Support for V2 assertion schema (urn:opentdf:system:metadata:v2)
# Go SDK supports V2, Java/JS only support V1
"assertion_schema_v2",
"autoconfigure",
"better-messages-2024",
"bulk_rewrap",
Expand Down Expand Up @@ -494,6 +497,92 @@ def skip_connectrpc_skew(encrypt_sdk: SDK, decrypt_sdk: SDK, pfs: PlatformFeatur
return False


def skip_assertion_schema_skew(
encrypt_sdk: SDK, decrypt_sdk: SDK, tdf_file: Path | None = None
):
"""Check assertion compatibility and skip/fail tests appropriately.

In xtest (testing environment): All assertion types and schemas MUST be explicitly
tested. Unknown assertions cause test FAILURE (not skip) to ensure comprehensive
test coverage and alert developers to new assertion types.

In real-world SDK usage: Unknown assertions are gracefully logged and skipped
to maintain forward compatibility (see tdf.go:1572).

This function inspects the TDF manifest to determine if the decrypt SDK can handle
the assertions created by the encrypt SDK, using feature detection via SDK.supports().

Known assertion schemas (identified by schema, not ID):
- "system-metadata-v1": System metadata V1 (all SDKs)
- "urn:opentdf:system:metadata:v2": System metadata V2 (Go SDK >= 0.10.0)
- "urn:nato:stanag:5636:A:1:elements:json": STANAG 5636 military standard (all SDKs)
- Key assertions: Identified by ID "assertion-key" (use custom schemas)

Args:
encrypt_sdk: The SDK used for encryption
decrypt_sdk: The SDK used for decryption
tdf_file: Path to the encrypted TDF file to inspect for actual assertions.
Must not be None.

Raises:
ValueError: If tdf_file is None (caller error).
pytest.fail: If TDF contains unknown assertion types or schemas.

Behavior:
- No assertions: test continues
- All compatible: test continues
- V2 schema without SDK support: test skips (temporary incompatibility)
- Unknown assertion type/schema: test FAILS (requires explicit handling)
"""
if tdf_file is None:
# Caller must provide a TDF file for inspection
raise ValueError(
"tdf_file cannot be None - must provide encrypted TDF to inspect"
)

m = manifest(tdf_file)
if not m.assertions:
# No assertions in TDF - nothing to verify, test can proceed
return

# Check all assertions - fail on unknown schemas to ensure comprehensive test coverage
# Assertions are identified by their schema, not their ID
for assertion in m.assertions:
schema = assertion.statement.schema_

# System metadata V2 schema - check if decrypt SDK supports it
if schema == "urn:opentdf:system:metadata:v2":
if not decrypt_sdk.supports("assertion_schema_v2"):
pytest.skip(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should fail on these, not skip. Perhaps log a message?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does a feature check first, then it will only skip if not supported.

f"TDF uses V2 assertion schema that {decrypt_sdk} doesn't support yet. "
f"Payload decryption works, but assertion verification will fail."
)
continue

# System metadata V1 schema or empty (legacy) - all SDKs support this
elif schema in ["system-metadata-v1", ""]:
continue

# STANAG 5636 assertions - all SDKs support this
elif schema == "urn:nato:stanag:5636:A:1:elements:json":
continue

# Key-based assertions use wildcard schema - all SDKs support this
# These can have various custom schemas, so we check with a pattern
elif assertion.id == "assertion-key":
# Key assertions are identified by ID since they use custom schemas
continue

# Unknown assertion schema - FAIL the test
else:
pytest.fail(
f"TDF uses unknown assertion schema: {schema!r} (id={assertion.id!r}). "
f"Known schemas: 'system-metadata-v1', 'urn:opentdf:system:metadata:v2', "
f"'urn:nato:stanag:5636:A:1:elements:json'. "
f"New assertion schemas must be explicitly tested and added to this function."
)


def select_target_version(
encrypt_sdk: SDK, decrypt_sdk: SDK
) -> container_version | None:
Expand Down
12 changes: 10 additions & 2 deletions xtest/test_tdfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ def test_tdf_assertions_unkeyed(
pfs = tdfs.PlatformFeatureSet()
if not in_focus & {encrypt_sdk, decrypt_sdk}:
pytest.skip("Not in focus")
target_mode = tdfs.select_target_version(encrypt_sdk, decrypt_sdk)
tdfs.skip_hexless_skew(encrypt_sdk, decrypt_sdk)
tdfs.skip_connectrpc_skew(encrypt_sdk, decrypt_sdk, pfs)
if not encrypt_sdk.supports("assertions"):
Expand All @@ -323,8 +324,10 @@ def test_tdf_assertions_unkeyed(
tmp_dir,
scenario="assertions",
az=assertion_file_no_keys,
target_mode=tdfs.select_target_version(encrypt_sdk, decrypt_sdk),
target_mode=target_mode,
)
# Check assertion schema compatibility after encryption
tdfs.skip_assertion_schema_skew(encrypt_sdk, decrypt_sdk, ct_file)
fname = ct_file.stem
rt_file = tmp_dir / f"{fname}.untdf"
decrypt_sdk.decrypt(ct_file, rt_file, "ztdf")
Expand All @@ -343,6 +346,7 @@ def test_tdf_assertions_with_keys(
pfs = tdfs.PlatformFeatureSet()
if not in_focus & {encrypt_sdk, decrypt_sdk}:
pytest.skip("Not in focus")
target_mode = tdfs.select_target_version(encrypt_sdk, decrypt_sdk)
tdfs.skip_hexless_skew(encrypt_sdk, decrypt_sdk)
tdfs.skip_connectrpc_skew(encrypt_sdk, decrypt_sdk, pfs)
if not encrypt_sdk.supports("assertions"):
Expand All @@ -356,8 +360,10 @@ def test_tdf_assertions_with_keys(
tmp_dir,
scenario="assertions-keys-roundtrip",
az=assertion_file_rs_and_hs_keys,
target_mode=tdfs.select_target_version(encrypt_sdk, decrypt_sdk),
target_mode=target_mode,
)
# Check assertion schema compatibility after encryption
tdfs.skip_assertion_schema_skew(encrypt_sdk, decrypt_sdk, ct_file)
fname = ct_file.stem
rt_file = tmp_dir / f"{fname}.untdf"

Expand Down Expand Up @@ -400,6 +406,8 @@ def test_tdf_assertions_422_format(
az=assertion_file_rs_and_hs_keys,
target_mode="4.2.2",
)
# Check assertion schema compatibility after encryption
tdfs.skip_assertion_schema_skew(encrypt_sdk, decrypt_sdk, ct_file)

fname = ct_file.stem
rt_file = tmp_dir / f"{fname}.untdf"
Expand Down