Skip to content

feat(xtest): add support for extended cryptographic mechanisms#401

Open
dmihalcik-virtru wants to merge 4 commits intoopentdf:mainfrom
dmihalcik-virtru:chore/xtest-extended-mechanisms
Open

feat(xtest): add support for extended cryptographic mechanisms#401
dmihalcik-virtru wants to merge 4 commits intoopentdf:mainfrom
dmihalcik-virtru:chore/xtest-extended-mechanisms

Conversation

@dmihalcik-virtru
Copy link
Member

Add fixtures and tests for additional key types:

  • ec:secp384r1 (key_e384)
  • ec:secp521r1 (key_e521)
  • rsa:4096 (key_r4096)

New fixtures in keys.py:

  • Three managed key fixtures with session-unique key_id suffixes
  • attribute_allof_with_extended_mechanisms fixture that creates an ALL_OF attribute with all three mechanisms and maps them to the test client for encryption

New test in test_abac.py:

  • test_key_mapping_extended_mechanisms verifies encryption and decryption with all three extended mechanisms, validating that the manifest contains the correct key IDs and KAS URLs

@dmihalcik-virtru dmihalcik-virtru requested review from a team as code owners February 12, 2026 16:47
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @dmihalcik-virtru, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the testing framework by integrating support for extended cryptographic mechanisms. It introduces new fixtures for managing ec:secp384r1, ec:secp521r1, and rsa:4096 keys, and a comprehensive test case to validate that encryption and decryption processes function correctly with these advanced key types. This ensures the system's compatibility and robustness with a broader range of cryptographic standards.

Highlights

  • Extended Cryptographic Mechanisms: Added support for extended cryptographic mechanisms, including ec:secp384r1, ec:secp521r1, and rsa:4096 key types.
  • New Pytest Fixtures: Introduced new pytest fixtures in keys.py to create and manage these extended keys and an attribute fixture that assigns them.
  • New Test Case: Implemented a new test in test_abac.py to verify encryption and decryption functionality with these extended mechanisms.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • xtest/fixtures/keys.py
    • Added key_e256 fixture for EC secp256r1 managed key.
    • Added key_e384 fixture for EC secp384r1 managed key.
    • Added key_e521 fixture for EC secp521r1 managed key.
    • Added key_r2048 fixture for RSA 2048 managed key.
    • Added key_r4096 fixture for RSA 4096 managed key.
    • Added attribute_allof_with_extended_mechanisms fixture to create an ALL_OF attribute with these extended mechanisms and map them to the test client.
  • xtest/test_abac.py
    • Added test_key_mapping_extended_mechanisms to verify encryption and decryption with the new extended mechanisms, including manifest validation.
Activity
  • No human activity (comments, reviews, etc.) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds support for extended cryptographic mechanisms, including new fixtures and tests. The implementation is a good step forward, but I've identified areas for improvement. Specifically, there's significant code duplication in the new fixtures within xtest/fixtures/keys.py that could be refactored for better maintainability. Additionally, the new test in xtest/test_abac.py has a bug in its assertions and a misleading docstring. My review includes suggestions to address these points, which will improve the code's quality and robustness.

Comment on lines 154 to 156
assert len(manifest.encryptionInformation.keyAccess) == 3

# Verify that all three key IDs are present in the manifest
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

There's a bug in the test logic. The attribute_allof_with_extended_mechanisms fixture provides 5 key IDs, so expected_kids will be a set of 5. However, you're asserting that keyAccess has a length of 3. This will cause the subsequent assertion manifest_kids == expected_kids to fail. The assertion should check for 5 key access objects.

Suggested change
assert len(manifest.encryptionInformation.keyAccess) == 3
# Verify that all three key IDs are present in the manifest
assert len(manifest.encryptionInformation.keyAccess) == 5
# Verify that all five key IDs are present in the manifest

Comment on lines 129 to 276
@pytest.fixture(scope="module")
def key_e256(
otdfctl: OpentdfCommandLineTool,
kas_entry_km1: abac.KasEntry,
root_key: str,
) -> abac.KasKey:
"""Get or create EC secp256r1 managed key on km1.

Key ID includes a hash of the root key to ensure that if the root key changes,
a new key will be created instead of reusing an incompatible one.
"""
pfs = tdfs.PlatformFeatureSet()
if "key_management" not in pfs.features:
pytest.skip("Key management feature is not enabled")

key_id = f"e256-{_key_id_suffix(root_key)}"
existing_keys = otdfctl.kas_registry_keys_list(kas_entry_km1)
key = next((k for k in existing_keys if k.key.key_id == key_id), None)
if key is None:
key = otdfctl.kas_registry_create_key(
kas_entry_km1,
key_id=key_id,
mode="local",
algorithm="ec:secp256r1",
wrapping_key=root_key,
wrapping_key_id="root",
)
return key


@pytest.fixture(scope="module")
def key_e384(
otdfctl: OpentdfCommandLineTool,
kas_entry_km1: abac.KasEntry,
root_key: str,
) -> abac.KasKey:
"""Get or create EC secp384r1 managed key on km1.

Key ID includes a hash of the root key to ensure that if the root key changes,
a new key will be created instead of reusing an incompatible one.
"""
pfs = tdfs.PlatformFeatureSet()
if "key_management" not in pfs.features:
pytest.skip("Key management feature is not enabled")

key_id = f"e384-{_key_id_suffix(root_key)}"
existing_keys = otdfctl.kas_registry_keys_list(kas_entry_km1)
key = next((k for k in existing_keys if k.key.key_id == key_id), None)
if key is None:
key = otdfctl.kas_registry_create_key(
kas_entry_km1,
key_id=key_id,
mode="local",
algorithm="ec:secp384r1",
wrapping_key=root_key,
wrapping_key_id="root",
)
return key


@pytest.fixture(scope="module")
def key_e521(
otdfctl: OpentdfCommandLineTool,
kas_entry_km2: abac.KasEntry,
root_key: str,
) -> abac.KasKey:
"""Get or create EC secp521r1 managed key on km2.

Key ID includes a hash of the root key to ensure that if the root key changes,
a new key will be created instead of reusing an incompatible one.
"""
pfs = tdfs.PlatformFeatureSet()
if "key_management" not in pfs.features:
pytest.skip("Key management feature is not enabled")

key_id = f"e521-{_key_id_suffix(root_key)}"
existing_keys = otdfctl.kas_registry_keys_list(kas_entry_km2)
key = next((k for k in existing_keys if k.key.key_id == key_id), None)
if key is None:
key = otdfctl.kas_registry_create_key(
kas_entry_km2,
key_id=key_id,
mode="local",
algorithm="ec:secp521r1",
wrapping_key=root_key,
wrapping_key_id="root",
)
return key


@pytest.fixture(scope="module")
def key_r2048(
otdfctl: OpentdfCommandLineTool,
kas_entry_km1: abac.KasEntry,
root_key: str,
) -> abac.KasKey:
"""Get or create RSA 2048 managed key on km1.

Key ID includes a hash of the root key to ensure that if the root key changes,
a new key will be created instead of reusing an incompatible one.
"""
pfs = tdfs.PlatformFeatureSet()
if "key_management" not in pfs.features:
pytest.skip("Key management feature is not enabled")

key_id = f"r2048-{_key_id_suffix(root_key)}"
existing_keys = otdfctl.kas_registry_keys_list(kas_entry_km1)
key = next((k for k in existing_keys if k.key.key_id == key_id), None)
if key is None:
key = otdfctl.kas_registry_create_key(
kas_entry_km1,
key_id=key_id,
mode="local",
algorithm="rsa:2048",
wrapping_key=root_key,
wrapping_key_id="root",
)
return key


@pytest.fixture(scope="module")
def key_r4096(
otdfctl: OpentdfCommandLineTool,
kas_entry_km1: abac.KasEntry,
root_key: str,
) -> abac.KasKey:
"""Get or create RSA 4096 managed key on km1.

Key ID includes a hash of the root key to ensure that if the root key changes,
a new key will be created instead of reusing an incompatible one.
"""
pfs = tdfs.PlatformFeatureSet()
if "key_management" not in pfs.features:
pytest.skip("Key management feature is not enabled")

key_id = f"r4096-{_key_id_suffix(root_key)}"
existing_keys = otdfctl.kas_registry_keys_list(kas_entry_km1)
key = next((k for k in existing_keys if k.key.key_id == key_id), None)
if key is None:
key = otdfctl.kas_registry_create_key(
kas_entry_km1,
key_id=key_id,
mode="local",
algorithm="rsa:4096",
wrapping_key=root_key,
wrapping_key_id="root",
)
return key
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The five new key fixtures (key_e256, key_e384, key_e521, key_r2048, key_r4096) contain a lot of duplicated code. To improve maintainability and reduce redundancy, this logic can be extracted into a single helper function. The fixtures can then call this helper with the specific parameters for each key type.

def _get_or_create_managed_key(
    otdfctl: OpentdfCommandLineTool,
    kas_entry: abac.KasEntry,
    root_key: str,
    key_id_prefix: str,
    algorithm: str,
) -> abac.KasKey:
    """Helper to get or create a managed key."""
    pfs = tdfs.PlatformFeatureSet()
    if "key_management" not in pfs.features:
        pytest.skip("Key management feature is not enabled")

    key_id = f"{key_id_prefix}-{_key_id_suffix(root_key)}"
    existing_keys = otdfctl.kas_registry_keys_list(kas_entry)
    key = next((k for k in existing_keys if k.key.key_id == key_id), None)
    if key is None:
        key = otdfctl.kas_registry_create_key(
            kas_entry,
            key_id=key_id,
            mode="local",
            algorithm=algorithm,
            wrapping_key=root_key,
            wrapping_key_id="root",
        )
    return key


@pytest.fixture(scope="module")
def key_e256(
    otdfctl: OpentdfCommandLineTool,
    kas_entry_km1: abac.KasEntry,
    root_key: str,
) -> abac.KasKey:
    """Get or create EC secp256r1 managed key on km1."""
    return _get_or_create_managed_key(
        otdfctl, kas_entry_km1, root_key, "e256", "ec:secp256r1"
    )


@pytest.fixture(scope="module")
def key_e384(
    otdfctl: OpentdfCommandLineTool,
    kas_entry_km1: abac.KasEntry,
    root_key: str,
) -> abac.KasKey:
    """Get or create EC secp384r1 managed key on km1."""
    return _get_or_create_managed_key(
        otdfctl, kas_entry_km1, root_key, "e384", "ec:secp384r1"
    )


@pytest.fixture(scope="module")
def key_e521(
    otdfctl: OpentdfCommandLineTool,
    kas_entry_km2: abac.KasEntry,
    root_key: str,
) -> abac.KasKey:
    """Get or create EC secp521r1 managed key on km2."""
    return _get_or_create_managed_key(
        otdfctl, kas_entry_km2, root_key, "e521", "ec:secp521r1"
    )


@pytest.fixture(scope="module")
def key_r2048(
    otdfctl: OpentdfCommandLineTool,
    kas_entry_km1: abac.KasEntry,
    root_key: str,
) -> abac.KasKey:
    """Get or create RSA 2048 managed key on km1."""
    return _get_or_create_managed_key(
        otdfctl, kas_entry_km1, root_key, "r2048", "rsa:2048"
    )


@pytest.fixture(scope="module")
def key_r4096(
    otdfctl: OpentdfCommandLineTool,
    kas_entry_km1: abac.KasEntry,
    root_key: str,
) -> abac.KasKey:
    """Get or create RSA 4096 managed key on km1."""
    return _get_or_create_managed_key(
        otdfctl, kas_entry_km1, root_key, "r4096", "rsa:4096"
    )

Comment on lines 303 to 346
# Create attribute with three values under ALL_OF
attr = otdfctl.attribute_create(
temporary_namespace,
"mechanism-select",
abac.AttributeRule.ALL_OF,
["ec:secp256r1", "ec:secp384r1", "ec:secp521r1", "rsa:2048", "rsa:4096"],
)
assert attr.values and len(attr.values) == 5
v_e256, v_e384, v_e521, v_r2048, v_r4096 = attr.values
assert v_e256.value == "ec:secp256r1"
assert v_e384.value == "ec:secp384r1"
assert v_e521.value == "ec:secp521r1"
assert v_r2048.value == "rsa:2048"
assert v_r4096.value == "rsa:4096"

# Ensure client has access to all values
sm1 = otdfctl.scs_map(otdf_client_scs, v_e256)
assert sm1.attribute_value.value == v_e256.value
sm2 = otdfctl.scs_map(otdf_client_scs, v_e384)
assert sm2.attribute_value.value == v_e384.value
sm3 = otdfctl.scs_map(otdf_client_scs, v_e521)
assert sm3.attribute_value.value == v_e521.value
sm4 = otdfctl.scs_map(otdf_client_scs, v_r2048)
assert sm4.attribute_value.value == v_r2048.value
sm5 = otdfctl.scs_map(otdf_client_scs, v_r4096)
assert sm5.attribute_value.value == v_r4096.value

# Assign keys to corresponding attribute values
otdfctl.key_assign_value(key_e256, v_e256)
otdfctl.key_assign_value(key_e384, v_e384)
otdfctl.key_assign_value(key_e521, v_e521)
otdfctl.key_assign_value(key_r2048, v_r2048)
otdfctl.key_assign_value(key_r4096, v_r4096)

return (
attr,
[
key_e256.key.key_id,
key_e384.key.key_id,
key_e521.key.key_id,
key_r2048.key.key_id,
key_r4096.key.key_id,
],
)
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The implementation of this fixture is repetitive and brittle. It relies on positional unpacking of attr.values, which assumes a specific order that is not guaranteed. This can be refactored to be more robust and data-driven by using a dictionary to map mechanisms to keys and iterating over it. This will make the code cleaner, easier to maintain, and less prone to breaking if the order of attribute values changes.

    mechanisms_to_keys = {
        "ec:secp256r1": key_e256,
        "ec:secp384r1": key_e384,
        "ec:secp521r1": key_e521,
        "rsa:2048": key_r2048,
        "rsa:4096": key_r4096,
    }
    mechanism_values = list(mechanisms_to_keys.keys())

    # Create attribute with values under ALL_OF
    attr = otdfctl.attribute_create(
        temporary_namespace,
        "mechanism-select",
        abac.AttributeRule.ALL_OF,
        mechanism_values,
    )
    assert attr.values and len(attr.values) == len(mechanism_values)

    # Create a mapping from value string to value object for easier lookup
    value_map = {v.value: v for v in attr.values}

    for mechanism, key in mechanisms_to_keys.items():
        value_obj = value_map[mechanism]

        # Ensure client has access to the value
        sm = otdfctl.scs_map(otdf_client_scs, value_obj)
        assert sm.attribute_value.value == value_obj.value

        # Assign key to corresponding attribute value
        otdfctl.key_assign_value(key, value_obj)

    return (
        attr,
        [key.key.key_id for key in mechanisms_to_keys.values()],
    )

pflynn-virtru
pflynn-virtru previously approved these changes Feb 12, 2026
Add fixtures and tests for additional key types:
- ec:secp384r1 (key_e384)
- ec:secp521r1 (key_e521)
- rsa:4096 (key_r4096)

New fixtures in keys.py:
- Three managed key fixtures with session-unique key_id suffixes
- attribute_allof_with_extended_mechanisms fixture that creates an
  ALL_OF attribute with all three mechanisms and maps them to the
  test client for encryption

New test in test_abac.py:
- test_key_mapping_extended_mechanisms verifies encryption and
  decryption with all three extended mechanisms, validating that
  the manifest contains the correct key IDs and KAS URLs
Add fixtures and tests for additional key types:
- ec:secp384r1 (key_e384)
- ec:secp521r1 (key_e521)
- rsa:4096 (key_r4096)

New fixtures in keys.py:
- Three managed key fixtures with session-unique key_id suffixes
- attribute_allof_with_extended_mechanisms fixture that creates an
  ALL_OF attribute with all three mechanisms and maps them to the
  test client for encryption

New test in test_abac.py:
- test_key_mapping_extended_mechanisms verifies encryption and
  decryption with all three extended mechanisms, validating that
  the manifest contains the correct key IDs and KAS URLs
@dmihalcik-virtru dmihalcik-virtru force-pushed the chore/xtest-extended-mechanisms branch from 75165b9 to ba15344 Compare February 12, 2026 22:34
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants