diff --git a/xtest/sdk/java/cli.sh b/xtest/sdk/java/cli.sh index cece9630..0a8725e0 100755 --- a/xtest/sdk/java/cli.sh +++ b/xtest/sdk/java/cli.sh @@ -96,6 +96,11 @@ if [ "$1" == "supports" ]; then exit 1 ;; + mechanism-mlkem) + set -o pipefail + java -jar "$SCRIPT_DIR"/cmdline.jar help encrypt | grep -i "mlkem:768" + exit $? + ;; mechanism-rsa-4096 | mechanism-ec-curves-384-521) # rsa4096 support in >= 0.13.0 set -o pipefail diff --git a/xtest/tdfs.py b/xtest/tdfs.py index 50f6e459..d09ca470 100644 --- a/xtest/tdfs.py +++ b/xtest/tdfs.py @@ -700,6 +700,64 @@ def skip_connectrpc_skew(encrypt_sdk: SDK, decrypt_sdk: SDK, pfs: PlatformFeatur return False +def _parse_semver(version: str) -> tuple[int, int, int] | None: + """Parse a version string (with optional 'v' prefix) into (major, minor, patch).""" + m = _version_re.match(version.lstrip("v")) + if not m: + return None + return (int(m.group(1)), int(m.group(2)), int(m.group(3))) + + +def _otdfctl_semver() -> tuple[int, int, int] | None: + """Parse otdfctl version from OTDFCTL_HEADS; None if unresolvable (main, dev, etc.).""" + oh = os.environ.get("OTDFCTL_HEADS", "[]") + try: + heads = json.loads(oh) + except json.JSONDecodeError: + return None + if not heads: + return None + return _parse_semver(str(heads[0])) + + +# go SDK ≤ this version used pre-ocrypto-0.13.0 hybrid KEM format (non-conformant). +_PQC_HYBRID_FORMAT_CUTOFF = (0, 33, 0) + + +def skip_pqc_hybrid_format_skew(encrypt_sdk: SDK | None = None) -> None: + """Skip if the encrypt SDK or otdfctl predates the lib/ocrypto v0.13.0 hybrid-KEM format. + + Two independent sources of format incompatibility: + - otdfctl ≤ 0.33.0: registers hybrid KAS keys in old non-conformant format. + - go encrypt SDK ≤ 0.33.0: produces hybrid KEM ciphertexts (wrappedKey) in old format. + Either causes opaque crypto failures when paired with a platform that has hybrid PQC + support enabled (new-format KAS); skip early with a clear message instead. + """ + pfs = get_platform_features() + if ( + "mechanism-xwing" not in pfs.features + and "mechanism-secpmlkem" not in pfs.features + ): + return + otdfctl_ver = _otdfctl_semver() + if otdfctl_ver is not None and otdfctl_ver <= _PQC_HYBRID_FORMAT_CUTOFF: + pytest.skip( + f"otdfctl v{'.'.join(map(str, otdfctl_ver))} predates lib/ocrypto v0.13.0; " + "hybrid key material format is incompatible with this platform" + ) + if ( + encrypt_sdk is not None + and encrypt_sdk.sdk == "go" + and encrypt_sdk.is_released() + ): + sdk_ver = _parse_semver(encrypt_sdk.version) + if sdk_ver is not None and sdk_ver <= _PQC_HYBRID_FORMAT_CUTOFF: + pytest.skip( + f"{encrypt_sdk} predates lib/ocrypto v0.13.0; " + "hybrid KEM ciphertext format is incompatible with this platform" + ) + + def select_target_version( encrypt_sdk: SDK, decrypt_sdk: SDK ) -> container_version | None: diff --git a/xtest/test_pqc.py b/xtest/test_pqc.py index b2fe7d6d..9ad25998 100644 --- a/xtest/test_pqc.py +++ b/xtest/test_pqc.py @@ -83,6 +83,7 @@ def test_xwing_roundtrip( ) tdfs.skip_connectrpc_skew(encrypt_sdk, decrypt_sdk, pfs) tdfs.skip_hexless_skew(encrypt_sdk, decrypt_sdk) + tdfs.skip_pqc_hybrid_format_skew(encrypt_sdk) attr, key_ids = attribute_with_xwing_key @@ -135,6 +136,7 @@ def test_xwing_with_ec_roundtrip( ) tdfs.skip_connectrpc_skew(encrypt_sdk, decrypt_sdk, pfs) tdfs.skip_hexless_skew(encrypt_sdk, decrypt_sdk) + tdfs.skip_pqc_hybrid_format_skew(encrypt_sdk) attr, key_ids = attribute_with_xwing_and_ec_keys @@ -199,6 +201,7 @@ def test_secpmlkem_3_roundtrip( ) tdfs.skip_connectrpc_skew(encrypt_sdk, decrypt_sdk, pfs) tdfs.skip_hexless_skew(encrypt_sdk, decrypt_sdk) + tdfs.skip_pqc_hybrid_format_skew(encrypt_sdk) attr, key_ids = attribute_with_secpmlkem_3_key @@ -277,6 +280,7 @@ def test_secpmlkem_5_roundtrip( ) tdfs.skip_connectrpc_skew(encrypt_sdk, decrypt_sdk, pfs) tdfs.skip_hexless_skew(encrypt_sdk, decrypt_sdk) + tdfs.skip_pqc_hybrid_format_skew(encrypt_sdk) attr, key_ids = attribute_with_secpmlkem_5_key