From 691cdfcc923dde16cce33dab3f324ff500ecb81a Mon Sep 17 00:00:00 2001 From: Paul Flynn Date: Mon, 20 Oct 2025 12:54:44 -0400 Subject: [PATCH 1/5] Add support for skipping tests with unsupported assertion schemas - Introduced `skip_assertion_schema_skew` to handle cases where the decrypt SDK does not support assertion schemas used by the encrypted TDF. - Consolidated `target_mode` usage to reduce redundancy. - Updated test logic to ensure assertion schema compatibility is verified. --- xtest/tdfs.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++ xtest/test_tdfs.py | 12 ++++++++-- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/xtest/tdfs.py b/xtest/tdfs.py index 893949ff..24b4bae0 100644 --- a/xtest/tdfs.py +++ b/xtest/tdfs.py @@ -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", @@ -445,6 +448,10 @@ def _uncached_supports(self, feature: feature_type) -> bool: return True case ("ns_grants", ("go" | "java")): return True + case ("assertion_schema_v2", "go"): + # Go SDK supports V2 assertion schema (urn:opentdf:system:metadata:v2) + # Java/JS SDKs don't support V2 yet - they only support V1 + return True case _: pass @@ -494,6 +501,59 @@ 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): + """Skip if the encrypted TDF uses assertion schemas that decrypt SDK can't verify. + + Checks the actual TDF manifest to see which assertion schemas were used, then + verifies if the decrypt SDK supports those schemas. This avoids hardcoding SDK + compatibility and makes the logic self-documenting through SDK.supports() checks. + + Currently, V2 assertion schema (urn:opentdf:system:metadata:v2) is not supported + by Java/JS SDKs. They can still decrypt the payload, but assertion verification fails. + + This function is generic and will automatically work when Java/JS add V2 support + or if new SDKs are added with different schema support. + + 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 schemas used. + """ + if tdf_file is None: + # Can't check without TDF file - caller error + return + + m = manifest(tdf_file) + if not m.assertions: + # No assertions - nothing to verify + return + + for assertion in m.assertions: + if assertion.id == "system-metadata": + schema = assertion.statement.schema_ + + # Check if decrypt SDK supports this specific schema + # V2 schema uses URN format + if schema == "urn:opentdf:system:metadata:v2": + if not decrypt_sdk.supports("assertion_schema_v2"): + pytest.skip( + f"TDF uses V2 assertion schema that {decrypt_sdk} doesn't support yet. " + f"Payload decryption works, but assertion verification will fail." + ) + + # V1 schema or empty (legacy) - all SDKs support this + elif schema in ["system-metadata-v1", ""]: + # All SDKs support V1 - continue + return + + # Unknown schema - check if decrypt SDK explicitly supports it + else: + # Unknown schema - be conservative and skip + pytest.skip( + f"TDF uses unknown assertion schema ({schema}) that {decrypt_sdk} may not support" + ) + + def select_target_version( encrypt_sdk: SDK, decrypt_sdk: SDK ) -> container_version | None: diff --git a/xtest/test_tdfs.py b/xtest/test_tdfs.py index ae93a580..195a2902 100644 --- a/xtest/test_tdfs.py +++ b/xtest/test_tdfs.py @@ -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"): @@ -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") @@ -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"): @@ -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" @@ -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" From b403d08869757660e128d05fb656df183e5444ed Mon Sep 17 00:00:00 2001 From: Paul Flynn Date: Mon, 20 Oct 2025 13:03:32 -0400 Subject: [PATCH 2/5] Raise error on missing TDF file and improve assertion schema handling --- xtest/tdfs.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/xtest/tdfs.py b/xtest/tdfs.py index 24b4bae0..c95b4a5c 100644 --- a/xtest/tdfs.py +++ b/xtest/tdfs.py @@ -518,14 +518,26 @@ def skip_assertion_schema_skew(encrypt_sdk: SDK, decrypt_sdk: SDK, tdf_file: Pat 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 schemas used. + Must not be None. + + Raises: + ValueError: If tdf_file is None (caller error). + + Behavior: + - If the TDF contains no assertions, no skip occurs (test continues). + - If all assertions use V1 schema, no skip occurs (V1 is universally supported). + - If any assertion uses V2 schema and decrypt SDK doesn't support it, test is skipped. + - If any assertion uses unknown schema, test is skipped for safety. """ if tdf_file is None: - # Can't check without TDF file - caller error - return + # 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 - nothing to verify + # No assertions in TDF - nothing to verify, test can proceed return for assertion in m.assertions: @@ -540,17 +552,19 @@ def skip_assertion_schema_skew(encrypt_sdk: SDK, decrypt_sdk: SDK, tdf_file: Pat f"TDF uses V2 assertion schema that {decrypt_sdk} doesn't support yet. " f"Payload decryption works, but assertion verification will fail." ) + # V2 supported - continue checking other assertions + continue # V1 schema or empty (legacy) - all SDKs support this elif schema in ["system-metadata-v1", ""]: - # All SDKs support V1 - continue - return + # All SDKs support V1 - continue checking other assertions + continue - # Unknown schema - check if decrypt SDK explicitly supports it + # Unknown schema - be conservative and skip else: - # Unknown schema - be conservative and skip pytest.skip( - f"TDF uses unknown assertion schema ({schema}) that {decrypt_sdk} may not support" + f"TDF uses unknown assertion schema ({schema}) " + f"that {decrypt_sdk} may not support" ) From cddf364086f36b980b1ea43ebd8d56267931ce11 Mon Sep 17 00:00:00 2001 From: Paul Flynn Date: Mon, 20 Oct 2025 13:07:27 -0400 Subject: [PATCH 3/5] fmt --- xtest/tdfs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xtest/tdfs.py b/xtest/tdfs.py index c95b4a5c..144967e7 100644 --- a/xtest/tdfs.py +++ b/xtest/tdfs.py @@ -501,7 +501,9 @@ 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): +def skip_assertion_schema_skew( + encrypt_sdk: SDK, decrypt_sdk: SDK, tdf_file: Path | None = None +): """Skip if the encrypted TDF uses assertion schemas that decrypt SDK can't verify. Checks the actual TDF manifest to see which assertion schemas were used, then From 6df013418315b594be83ccee9587bb3b7d5aba01 Mon Sep 17 00:00:00 2001 From: Paul Flynn Date: Mon, 20 Oct 2025 14:08:42 -0400 Subject: [PATCH 4/5] Add support for handling assertion_schema_v2 in Go SDK and update test logic --- xtest/sdk/go/cli.sh | 8 ++++++++ xtest/tdfs.py | 4 ---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/xtest/sdk/go/cli.sh b/xtest/sdk/go/cli.sh index 425582c5..50649e47 100755 --- a/xtest/sdk/go/cli.sh +++ b/xtest/sdk/go/cli.sh @@ -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 diff --git a/xtest/tdfs.py b/xtest/tdfs.py index 144967e7..09c01d64 100644 --- a/xtest/tdfs.py +++ b/xtest/tdfs.py @@ -448,10 +448,6 @@ def _uncached_supports(self, feature: feature_type) -> bool: return True case ("ns_grants", ("go" | "java")): return True - case ("assertion_schema_v2", "go"): - # Go SDK supports V2 assertion schema (urn:opentdf:system:metadata:v2) - # Java/JS SDKs don't support V2 yet - they only support V1 - return True case _: pass From e91d07617a77ce1018376515a2d3ebe86174a4a2 Mon Sep 17 00:00:00 2001 From: Paul Flynn Date: Mon, 20 Oct 2025 14:39:20 -0400 Subject: [PATCH 5/5] Improve assertion schema handling to ensure comprehensive test coverage and update test failure logic for unknown schemas --- xtest/tdfs.py | 89 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/xtest/tdfs.py b/xtest/tdfs.py index 09c01d64..7dfe548d 100644 --- a/xtest/tdfs.py +++ b/xtest/tdfs.py @@ -500,32 +500,39 @@ def skip_connectrpc_skew(encrypt_sdk: SDK, decrypt_sdk: SDK, pfs: PlatformFeatur def skip_assertion_schema_skew( encrypt_sdk: SDK, decrypt_sdk: SDK, tdf_file: Path | None = None ): - """Skip if the encrypted TDF uses assertion schemas that decrypt SDK can't verify. + """Check assertion compatibility and skip/fail tests appropriately. - Checks the actual TDF manifest to see which assertion schemas were used, then - verifies if the decrypt SDK supports those schemas. This avoids hardcoding SDK - compatibility and makes the logic self-documenting through SDK.supports() checks. + 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. - Currently, V2 assertion schema (urn:opentdf:system:metadata:v2) is not supported - by Java/JS SDKs. They can still decrypt the payload, but assertion verification fails. + In real-world SDK usage: Unknown assertions are gracefully logged and skipped + to maintain forward compatibility (see tdf.go:1572). - This function is generic and will automatically work when Java/JS add V2 support - or if new SDKs are added with different schema support. + 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 schemas used. + 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: - - If the TDF contains no assertions, no skip occurs (test continues). - - If all assertions use V1 schema, no skip occurs (V1 is universally supported). - - If any assertion uses V2 schema and decrypt SDK doesn't support it, test is skipped. - - If any assertion uses unknown schema, test is skipped for safety. + - 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 @@ -538,32 +545,42 @@ def skip_assertion_schema_skew( # 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: - if assertion.id == "system-metadata": - schema = assertion.statement.schema_ - - # Check if decrypt SDK supports this specific schema - # V2 schema uses URN format - if schema == "urn:opentdf:system:metadata:v2": - if not decrypt_sdk.supports("assertion_schema_v2"): - pytest.skip( - f"TDF uses V2 assertion schema that {decrypt_sdk} doesn't support yet. " - f"Payload decryption works, but assertion verification will fail." - ) - # V2 supported - continue checking other assertions - continue - - # V1 schema or empty (legacy) - all SDKs support this - elif schema in ["system-metadata-v1", ""]: - # All SDKs support V1 - continue checking other assertions - continue - - # Unknown schema - be conservative and skip - else: + 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( - f"TDF uses unknown assertion schema ({schema}) " - f"that {decrypt_sdk} may not support" + 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(