Skip to content

Guard cryptography internals monkey patch and add ECC verification tests#15

Draft
Copilot wants to merge 5 commits intodependabot/pip/cryptography-46.0.5from
copilot/make-keys-py-resilient
Draft

Guard cryptography internals monkey patch and add ECC verification tests#15
Copilot wants to merge 5 commits intodependabot/pip/cryptography-46.0.5from
copilot/make-keys-py-resilient

Conversation

Copy link

Copilot AI commented Feb 11, 2026

Cryptography 46.0.0+ removed internal OpenSSL EC APIs that pymrtd monkey-patches to support unnamed elliptic curves. This PR guards the import and conditionally applies the patch based on availability.

Changes

src/pymrtd/pki/keys.py

  • Wrap internal cryptography.hazmat.backends.openssl.ec imports in try/except ImportError
  • Apply monkey patch only when internals are present (cryptography <46)
  • Replace bare except with except Exception in patched initializer
  • Add logging at debug/info level for patch application status

tests/test_ecc_verification.py (new)

  • Add test coverage for X9.62 (DER) ECDSA signature verification
  • Add test coverage for plain (r||s) ECDSA signature verification (ICAO 9303 format)
  • Both tests use SECP256R1 with SHA256

.github/workflows/pytest.yml (new)

  • Test matrix: Python 3.9-3.12 × cryptography [41.0.3, 46.0.5]
  • Validates behavior with and without internal API availability

Example

# When cryptography <46 (internals available):
# INFO:pymrtd.pki.keys:Applied _EllipticCurvePublicKey monkey patch

# When cryptography >=46 (internals removed):
# DEBUG:pymrtd.pki.keys:cryptography internals not available; skipping EC monkey patch

Both paths maintain identical external behavior for verification operations.

Original prompt

Summary

Create a new branch (based on dependabot/pip/cryptography-46.0.5) and open a pull request that: (1) makes keys.py resilient to cryptography internals being removed (guard the internal import and only apply the monkey patch when internals are available), (2) adds ECC verification tests that exercise both X9.62 (DER) signatures and plain (r||s) ECDSA signatures using generated keys, and (3) ensures the tests run in CI by adding a GitHub Actions workflow that runs pytest. The PR should target the repository ZeroPass/pymrtd.

Files to add/modify

  1. Modify src/pymrtd/pki/keys.py
  • Wrap the current unguarded internal import and monkey-patch in a try/except ImportError block. Keep behavior identical when internals are present. Use logging at debug/info level when skipping or applying the patch. Replace bare except with Exception for runtime errors inside the patched initializer. The change must be minimal and preserve current behavior for older cryptography versions.

Suggested patch (style should be adapted to the repo):

@@
-# Monkey patch _EllipticCurvePublicKey to allow unnamed curves (explicit params)
-from cryptography.hazmat.backends.openssl.ec import ( #pylint: disable=ungrouped-imports,wrong-import-position
-    _EllipticCurvePublicKey,
-    _mark_asn1_named_ec_curve,
-    _ec_key_curve_sn,
-    _sn_to_elliptic_curve
-)
-
-def _new_ec_pub_key_init(self, backend, ec_key_cdata, evp_pkey):
-    #pylint: disable=protected-access
-    self._backend  = backend
-    self._ec_key   = ec_key_cdata
-    self._evp_pkey = evp_pkey
-    try:
-        _mark_asn1_named_ec_curve(backend, ec_key_cdata)
-        sn = _ec_key_curve_sn(backend, ec_key_cdata)
-        self._curve = _sn_to_elliptic_curve(backend, sn)
-    except: #pylint: disable=bare-except
-        self._curve = None
-
-_EllipticCurvePublicKey.__init__ = _new_ec_pub_key_init
+import logging
+logger = logging.getLogger(__name__)
+
+# Monkey patch _EllipticCurvePublicKey to allow unnamed curves (explicit params)
+# Apply only when cryptography internal API is present; skip otherwise (cryptography >=46)
+try:
+    from cryptography.hazmat.backends.openssl.ec import (  # pylint: disable=ungrouped-imports,wrong-import-position
+        _EllipticCurvePublicKey,
+        _mark_asn1_named_ec_curve,
+        _ec_key_curve_sn,
+        _sn_to_elliptic_curve,
+    )
+except ImportError:
+    logger.debug("cryptography internals not available; skipping EC monkey patch")
+else:
+    def _new_ec_pub_key_init(self, backend, ec_key_cdata, evp_pkey):
+        # pylint: disable=protected-access
+        self._backend = backend
+        self._ec_key = ec_key_cdata
+        self._evp_pkey = evp_pkey
+        try:
+            _mark_asn1_named_ec_curve(backend, ec_key_cdata)
+            sn = _ec_key_curve_sn(backend, ec_key_cdata)
+            self._curve = _sn_to_elliptic_curve(backend, sn)
+        except Exception:
+            self._curve = None
+
+    _EllipticCurvePublicKey.__init__ = _new_ec_pub_key_init
+    logger.info("Applied _EllipticCurvePublicKey monkey patch (if internals present)")
  1. Add tests/tests/test_ecc_verification.py
  • Add a pytest file that generates an EC keypair (SECP256R1), signs a message using cryptography's ECDSA (X9.62) and verifies via pymrtd PublicKey.verifySignature with non-plain and plain signature algorithm flows.
  • The file should match the test content agreed in the conversation; it must import pymrtd.pki.keys and use keys.PublicKey.load, keys.SignatureAlgorithm, and keys.ECDSA_X962_Signature.

Test file contents:

import importlib
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.backends import default_backend

from pymrtd.pki import keys


def _generate_key_sign_and_serialize():
    priv = ec.generate_private_key(ec.SECP256R1(), default_backend())
    message = b"pymrtd test message for ECDSA verification"
    signature = priv.sign(message, ec.ECDSA(hashes.SHA256()))
    pub = priv.public_key()
    pub_der = pub.public_bytes(
        encoding=serialization.Encoding.DER,
        format=serialization.PublicFormat.SubjectPublicKeyInfo,
    )
    return message, signature, pub_der


def test_ec_verify_x962_signature():
    message, signature, pub_der = _generate_key_sign_and_serialize()
    pub_key = keys.PublicKey.load(pub_der)
    sig_algo = keys.SignatureAlgorithm({"algorithm": "sha256_ecdsa"})
    assert pub_key.verifySignature(message, signature, sig_algo)


def test_ec_verify_plain_signature():
    message, signature, pub_der = _generate_key_sign_and_serialize()
    x962 = keys.ECDSA_X962_Signature.load(signature)
    plain_sig = x962.toPlain()
    pub_key = keys.PublicKey.load(pub_der)
    sig_algo_plain = keys.SignatureAlgorithm({"algorithm": "sha256_plain_ecdsa"})
    assert pub_key.verifySignature(message, plain_sig, sig_algo_plain)
`...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---Let Copilot coding agent [set things up for you](https://github.com/ZeroPass/pymrtd/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 4 commits February 11, 2026 21:13
… workflow

Co-authored-by: smlu <4377556+smlu@users.noreply.github.com>
…mport

Co-authored-by: smlu <4377556+smlu@users.noreply.github.com>
Co-authored-by: smlu <4377556+smlu@users.noreply.github.com>
Co-authored-by: smlu <4377556+smlu@users.noreply.github.com>
Copilot AI changed the title [WIP] Make keys.py resilient to cryptography internals removal Guard cryptography internals monkey patch and add ECC verification tests Feb 11, 2026
Copilot AI requested a review from smlu February 11, 2026 21:20
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