Skip to content

Conversation

@stktyagi
Copy link
Member

@stktyagi stktyagi commented Jan 11, 2026

Updated KEY_LENGTH_CHOICES to include 256, 384 and 521 bits to support Elliptic Curve algorithms.

Fixes #118

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

Reference to Existing Issue

Closes #118.

Description of Changes

Added support for importing and generating ECDSA certificates, resolving the KeyError when importing certificates with Elliptic Curve algorithms, while preventing confusion between RSA and EC key lengths.

Updated KEY_LENGTH_CHOICES to include 224, 256, and 384 bits to support Elliptic Curve algorithms.

Fixes #118
@coderabbitai
Copy link

coderabbitai bot commented Jan 11, 2026

📝 Walkthrough

Walkthrough

Adds explicit ECDSA (EC) support alongside RSA in certificate handling. Introduces public constants RSA_KEY_LENGTHS and EC_KEY_LENGTHS, expands KEY_LENGTH_CHOICES to include EC (256/384/521) and RSA (1024/2048/4096) sizes, and updates model logic so generation, import, serialization, digest normalization, and validation branch on key type. clean() enforces allowed lengths; _generate() maps EC lengths to curves and uses PKCS8 for EC private keys while retaining TraditionalOpenSSL for RSA; _import() detects certificate public-key type/size, validates alignment with selected length, updates self.key_length, and raises on mismatches. Tests and migrations updated accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Model as Certificate Model
    participant Validator as Validation Logic
    participant Crypto as Cryptography Lib

    User->>Model: request _generate(params)
    Model->>Validator: validate key_length ∈ EC_KEY_LENGTHS ∪ RSA_KEY_LENGTHS
    Validator-->>Model: valid / error
    alt EC length
        Model->>Crypto: generate EC key (curve ← key_length)
        Crypto-->>Model: EC PrivateKey
        Model->>Crypto: serialize private key (PKCS8)
    else RSA length
        Model->>Crypto: generate RSA key (bits = key_length)
        Crypto-->>Model: RSA PrivateKey
        Model->>Crypto: serialize private key (TraditionalOpenSSL)
    end
    Model->>Crypto: normalize digest & sign certificate
    Crypto-->>Model: Signed certificate PEM
    Model-->>User: certificate PEM + private key PEM
Loading
sequenceDiagram
    participant User
    participant Model as Certificate Model
    participant Parser as Cert Parser
    participant Validator as Validation Logic

    User->>Model: request _import(certificate_bytes)
    Model->>Parser: parse certificate -> public key + sig algorithm
    Parser-->>Model: PublicKey obj, signature algorithm
    Model->>Validator: determine key_type and actual_length from PublicKey
    Validator-->>Model: actual_length / unsupported error
    Model->>Validator: ensure selected key_length (if set) matches detected type/length
    Validator-->>Model: valid / ValidationError
    Model->>Model: set self.key_length = actual_length
    Model-->>User: import success or ValidationError
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • nemesifier
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main feature: adding support for importing ECDSA certificates, which directly corresponds to the primary changes in the PR.
Description check ✅ Passed The description covers the main changes, references the linked issue #118, includes a completed checklist with manual testing and test cases, though documentation is marked incomplete.
Linked Issues check ✅ Passed The PR addresses issue #118 by adding EC key support (256/384/521 bits), implementing ECDSA certificate import/generation, updating KEY_LENGTH_CHOICES, and mapping EC signature algorithms to prevent KeyError on import.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the PR objectives: EC algorithm support, key length choices expansion, migration updates, and comprehensive test coverage for ECDSA functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54316bd and 8b56b13.

📒 Files selected for processing (5)
  • django_x509/base/models.py
  • django_x509/migrations/0011_alter_ca_key_length_alter_cert_key_length.py
  • django_x509/tests/test_ca.py
  • django_x509/tests/test_cert.py
  • tests/openwisp2/sample_x509/migrations/0004_alter_ca_key_length_alter_cert_key_length_and_more.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: stktyagi
Repo: openwisp/django-x509 PR: 201
File: tests/openwisp2/sample_x509/migrations/0003_alter_ca_key_length_alter_cert_key_length_and_more.py:18-26
Timestamp: 2026-01-16T21:54:34.275Z
Learning: Do not suggest adding 224-bit ECDSA support in django-x509. SECP224R1 provides only 112 bits of cryptographic strength and is deprecated by NIST SP 800-131A Rev.3 for asymmetric use beyond December 31, 2030. Modern standards recommend 256-bit curves (SECP256R1/P-256) as the minimum.
📚 Learning: 2026-01-16T21:54:25.644Z
Learnt from: stktyagi
Repo: openwisp/django-x509 PR: 201
File: tests/openwisp2/sample_x509/migrations/0003_alter_ca_key_length_alter_cert_key_length_and_more.py:18-26
Timestamp: 2026-01-16T21:54:25.644Z
Learning: Do not review or suggest adding 224-bit ECDSA support in django-x509 code. SECP224R1 provides only 112 bits of cryptographic strength and is deprecated for asymmetric use by NIST SP 800-131A Rev.3 after 2030. Prefer and enforce the use of modern curves (e.g., SECP256R1/P-256) as the minimum. This guideline applies broadly to Python files in this repository; ensure reviews advocate using at least 256-bit curves and document any cryptographic choices accordingly.

Applied to files:

  • tests/openwisp2/sample_x509/migrations/0004_alter_ca_key_length_alter_cert_key_length_and_more.py
  • django_x509/migrations/0011_alter_ca_key_length_alter_cert_key_length.py
  • django_x509/base/models.py
  • django_x509/tests/test_ca.py
  • django_x509/tests/test_cert.py
🧬 Code graph analysis (3)
tests/openwisp2/sample_x509/migrations/0004_alter_ca_key_length_alter_cert_key_length_and_more.py (2)
django_x509/migrations/0011_alter_ca_key_length_alter_cert_key_length.py (1)
  • Migration (7-50)
django_x509/base/models.py (1)
  • default_key_length (74-79)
django_x509/migrations/0011_alter_ca_key_length_alter_cert_key_length.py (2)
tests/openwisp2/sample_x509/migrations/0004_alter_ca_key_length_alter_cert_key_length_and_more.py (1)
  • Migration (7-68)
django_x509/base/models.py (1)
  • default_key_length (74-79)
django_x509/tests/test_ca.py (3)
django_x509/base/models.py (5)
  • x509 (201-206)
  • save (192-198)
  • pkey (231-240)
  • renew (661-668)
  • renew (694-704)
django_x509/models.py (1)
  • Ca (6-13)
tests/openwisp2/sample_x509/models.py (1)
  • Ca (13-19)
🪛 Gitleaks (8.30.0)
django_x509/tests/test_cert.py

[high] 44-71: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)


[high] 100-127: Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.

(private-key)

🪛 Ruff (0.14.13)
tests/openwisp2/sample_x509/migrations/0004_alter_ca_key_length_alter_cert_key_length_and_more.py

9-11: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


13-68: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

django_x509/migrations/0011_alter_ca_key_length_alter_cert_key_length.py

9-11: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


13-50: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

🔇 Additional comments (15)
tests/openwisp2/sample_x509/migrations/0004_alter_ca_key_length_alter_cert_key_length_and_more.py (1)

1-68: LGTM! Migration correctly adds ECDSA support.

The migration properly:

  • Adds ECDSA key sizes (256, 384, 521) for EC curve support
  • Removes 512-bit RSA (aligned with the commit to drop weak RSA key sizes)
  • Keeps choices consistent across all three models (ca, cert, customcert)
  • Matches the main app migration (django_x509/migrations/0011) choices

The RUF012 static analysis hints are false positives—dependencies and operations are standard Django migration class attributes that don't require ClassVar annotations.

django_x509/tests/test_ca.py (3)

5-6: LGTM!

Import additions for hashes, serialization, ec, and rsa are appropriate for the new ECDSA lifecycle testing.


37-88: LGTM! Updated test certificates align with security improvements.

The test certificates have been updated from 512-bit RSA to 2048-bit RSA, which aligns with the removal of weak 512-bit RSA support mentioned in the commit messages and reflects current cryptographic best practices.


717-769: Well-structured ECDSA lifecycle test covering all supported curves.

The test correctly:

  • Uses subTest for better test isolation and reporting
  • Tests all three EC curves (256, 384, 521) with curve-appropriate digests (SHA256, SHA384, SHA512)
  • Validates both import and generation paths
  • Verifies renewal regenerates keys and certificates
  • Uses datetime.now(dt_timezone.utc) as recommended

Previous review feedback has been addressed (curve-appropriate digests, full_clean() calls, correct original_key comparison).

django_x509/migrations/0011_alter_ca_key_length_alter_cert_key_length.py (1)

1-50: LGTM! Migration correctly updates key_length choices for ECDSA support.

The migration properly:

  • Adds EC curve sizes (256, 384, 521) with "(ECDSA)" labels
  • Retains RSA sizes (1024, 2048, 4096) with "(RSA)" labels
  • Correctly excludes deprecated 512-bit RSA and 224-bit EC
  • Uses default_key_length function to avoid hardcoding defaults in migrations

The static analysis hints about RUF012 (mutable class attributes) are false positives for Django migrations, which intentionally use mutable dependencies and operations lists.

django_x509/base/models.py (6)

21-31: LGTM! Well-organized key length constants.

The separation of RSA_KEY_LENGTHS and EC_KEY_LENGTHS constants improves code clarity and enables targeted validation. The choices correctly:

  • Include descriptive labels distinguishing ECDSA from RSA
  • Exclude deprecated 224-bit EC (per NIST SP 800-131A Rev.3)
  • Exclude weak 512-bit RSA

Based on learnings, 224-bit ECDSA is intentionally omitted.


172-190: LGTM! Validation logic ensures only supported key lengths are accepted.

The unified validation against both RSA_KEY_LENGTHS and EC_KEY_LENGTHS provides a safety net beyond Django's field-level choice validation, which is useful for programmatic access.


293-310: LGTM! EC key generation with proper curve mapping.

The implementation correctly maps key lengths to their corresponding NIST curves and raises a clear validation error for unsupported EC lengths. The fallback to RSA generation for non-EC lengths maintains backward compatibility.


334-339: Robust digest normalization handles various algorithm name formats.

The normalization logic correctly strips common prefixes/suffixes from digest algorithm names (e.g., ecdsa-with-SHA256sha256, sha256WithRSAEncryptionsha256). This ensures compatibility when importing certificates signed with different algorithm naming conventions.


348-356: Appropriate private key format selection.

Using PKCS8 for EC keys and TraditionalOpenSSL for RSA keys is the correct approach. PKCS8 is the modern standard that properly handles EC keys, while TraditionalOpenSSL maintains backward compatibility for existing RSA key consumers.


382-417: Comprehensive import validation with clear error messages.

The import logic properly:

  • Detects RSA vs EC key types from the certificate
  • Validates that the selected key_length matches the certificate's algorithm type
  • Rejects unsupported key types (DSA, Ed25519, etc.) with a clear error message
  • Validates EC curve sizes against supported lengths
  • Updates key_length to the actual certificate value

This addresses previous review feedback about handling unsupported key types.

django_x509/tests/test_cert.py (4)

5-6: LGTM!

Import additions for hashes, serialization, ec, and rsa support the new ECDSA lifecycle testing.


21-128: Updated test fixtures align with strengthened key requirements.

The test certificates have been regenerated with 2048-bit RSA keys. The Gitleaks warnings about private keys are false positives—these are intentionally embedded test fixtures for certificate import testing, not production secrets.


218-225: LGTM! Assertions updated to match new test certificate attributes.

The expected values correctly reflect the regenerated 2048-bit RSA test certificate's properties.


544-597: Comprehensive ECDSA lifecycle test for Cert model.

The test properly validates the full EC certificate workflow:

  • Creates EC-based CAs for each curve size
  • Tests certificate import with externally-generated EC keys
  • Verifies CA signing works correctly (uses ca.pkey for signing)
  • Tests certificate generation through the model
  • Validates renewal behavior

This fulfills the reviewer request to add ECDSA tests in test_cert.py as well.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coveralls
Copy link

coveralls commented Jan 11, 2026

Coverage Status

coverage: 95.0% (-0.4%) from 95.411%
when pulling 8b56b13 on issues/118-import-edcsa-existing-ca-KeyError
into 7f43fa8 on master.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @django_x509/base/models.py:
- Around line 21-37: The EC P-521 curve is missing from the choices causing
validation and unreachable generation code; update KEY_LENGTH_CHOICES to include
("521","521") and add "521" to EC_KEY_LENGTHS so the field validation matches
the curve mapping used in the _generate() method (which references "521":
ec.SECP521R1()), ensuring imports/validation and generation paths are
consistent.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f43fa8 and d578ea4.

📒 Files selected for processing (3)
  • django_x509/base/models.py
  • django_x509/migrations/0010_ca_key_type_cert_key_type_alter_ca_key_length_and_more.py
  • tests/openwisp2/sample_x509/migrations/0003_ca_key_type_cert_key_type_customcert_key_type_and_more.py
🧰 Additional context used
🧬 Code graph analysis (2)
django_x509/migrations/0010_ca_key_type_cert_key_type_alter_ca_key_length_and_more.py (2)
tests/openwisp2/sample_x509/migrations/0003_ca_key_type_cert_key_type_customcert_key_type_and_more.py (1)
  • Migration (7-101)
django_x509/base/models.py (1)
  • default_key_length (80-85)
tests/openwisp2/sample_x509/migrations/0003_ca_key_type_cert_key_type_customcert_key_type_and_more.py (2)
django_x509/migrations/0010_ca_key_type_cert_key_type_alter_ca_key_length_and_more.py (1)
  • Migration (7-72)
django_x509/base/models.py (1)
  • default_key_length (80-85)
🪛 Ruff (0.14.10)
django_x509/migrations/0010_ca_key_type_cert_key_type_alter_ca_key_length_and_more.py

9-11: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


13-72: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

tests/openwisp2/sample_x509/migrations/0003_ca_key_type_cert_key_type_customcert_key_type_and_more.py

9-11: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


13-101: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

🔇 Additional comments (7)
django_x509/migrations/0010_ca_key_type_cert_key_type_alter_ca_key_length_and_more.py (1)

1-72: Migration structure looks correct.

The migration properly adds the key_type field and updates key_length choices to support both RSA and EC key types. The field definitions are consistent with the base model constants.

The RUF012 static analysis warnings about ClassVar can be safely ignored—Django migrations conventionally use mutable class attributes for dependencies and operations.

django_x509/base/models.py (5)

162-164: Key type field and cross-field validation look correct.

The new key_type field is properly defined with appropriate choices and default. The validation logic correctly prevents mismatched combinations—RSA keys can't use EC lengths (224/256/384) and EC keys can't use RSA lengths (512/1024/2048/4096).

Also applies to: 195-203


306-325: EC key generation logic is well-implemented.

The implementation correctly branches based on key_type, using standard NIST curves (SECP224R1, SECP256R1, SECP384R1, SECP521R1) for EC key generation. Error handling for unsupported key types and lengths is appropriate.


349-354: Digest algorithm normalization correctly handles ECDSA signatures.

This change resolves the KeyError from issue #118 by stripping the "ecdsa-with-" prefix from digest algorithm names during certificate import, allowing proper mapping to the hash algorithm.


362-370: Private key format selection is appropriate.

Using PKCS8 format for EC keys (modern standard, better interoperability) while retaining TraditionalOpenSSL for RSA keys (backward compatibility) is a sensible approach.


402-408: Import logic correctly detects key type from certificate.

The implementation properly identifies RSA vs EC keys using isinstance checks and extracts the appropriate key size. For EC keys, curve.key_size correctly returns the curve's bit length.

tests/openwisp2/sample_x509/migrations/0003_ca_key_type_cert_key_type_customcert_key_type_and_more.py (1)

1-101: Test migration is consistent with main migration.

The sample app migration correctly mirrors the main django_x509 migration, adding key_type to all three models (CA, Cert, CustomCert) and updating key_length choices. Field definitions are consistent.

The RUF012 static analysis warnings can be ignored—this is standard Django migration structure.

Added P-521 curve in KEY_LENGTH_CHOICES and EC_KEY_LENGTHS

Fixes #118
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @django_x509/base/models.py:
- Around line 196-199: The ValidationError raised when self.key_type == "rsa"
and self.key_length in EC_KEY_LENGTHS is misleading; update the error message to
clearly state that the selected key length is an EC-specific value and
incompatible with RSA (include the offending value via self.key_length), and
guide the user to choose a valid RSA key length or switch to an EC key type;
update the ValidationError in this branch (the check referencing EC_KEY_LENGTHS)
to include that clarification and optionally list acceptable RSA lengths or
reference the EC lengths constant for clarity.
- Around line 403-409: When cert.public_key() returns a type other than
rsa.RSAPublicKey or ec.EllipticCurvePublicKey, self.key_type and self.key_length
are left unset; add an else branch after the EC check that sets safe defaults
and records the actual key class: set self.key_type =
cert.public_key().__class__.__name__.lower() (or "unknown" if unavailable), set
self.key_length = "" (or None) to avoid validation errors, and emit a warning
via the module/class logger so imports of DSA/Ed25519/etc. are recorded rather
than leaving fields empty.
🧹 Nitpick comments (2)
django_x509/migrations/0011_alter_ca_key_length_alter_cert_key_length.py (1)

18-27: Consider reordering key length choices for better UX.

The key_length choices mix EC lengths (224, 256, 384, 521) with RSA lengths (512, 1024, 2048, 4096) in a grouped-by-type order rather than numerical order. While functionally correct, this may confuse users selecting values in UI dropdowns, especially since the key_type context isn't visible in the choice value itself.

Consider either:

  • Sorting numerically: 224, 256, 384, 512, 521, 1024, 2048, 4096
  • Adding prefixes to clarify type: ("224", "224 (EC)"), ("512", "512 (RSA)"), etc.

Also applies to: 38-47

tests/openwisp2/sample_x509/migrations/0004_alter_ca_key_length_alter_cert_key_length_and_more.py (1)

18-27: Consider reordering key length choices for better UX.

The key_length choices are grouped by type (EC: 224, 256, 384, 521; RSA: 512, 1024, 2048, 4096) rather than sorted numerically. This non-intuitive ordering may confuse users in UI dropdowns where the key_type context isn't visible.

Consider numerical sorting or type-prefixed labels as suggested in the main migration (0011).

Also applies to: 38-47, 58-67

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d578ea4 and c2f3be8.

📒 Files selected for processing (3)
  • django_x509/base/models.py
  • django_x509/migrations/0011_alter_ca_key_length_alter_cert_key_length.py
  • tests/openwisp2/sample_x509/migrations/0004_alter_ca_key_length_alter_cert_key_length_and_more.py
🧰 Additional context used
🧬 Code graph analysis (1)
tests/openwisp2/sample_x509/migrations/0004_alter_ca_key_length_alter_cert_key_length_and_more.py (2)
django_x509/migrations/0011_alter_ca_key_length_alter_cert_key_length.py (1)
  • Migration (7-54)
django_x509/base/models.py (1)
  • default_key_length (81-86)
🪛 Ruff (0.14.10)
django_x509/migrations/0011_alter_ca_key_length_alter_cert_key_length.py

9-11: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


13-54: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

tests/openwisp2/sample_x509/migrations/0004_alter_ca_key_length_alter_cert_key_length_and_more.py

9-11: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


13-74: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

🔇 Additional comments (5)
django_x509/base/models.py (5)

21-30: LGTM! EC key lengths added correctly.

The added EC lengths (224, 256, 384, 521) correspond to standard NIST curves (SECP224R1, SECP256R1, SECP384R1, SECP521R1) and are correctly integrated with existing RSA lengths.


32-38: LGTM! Key type support well-structured.

The new constants (KEY_TYPE_CHOICES, RSA_KEY_LENGTHS, EC_KEY_LENGTHS) and key_type field are well-designed. The default value "rsa" ensures backward compatibility with existing data.

Also applies to: 163-165


350-354: LGTM! Digest normalization addresses issue #118.

The digest name normalization correctly strips EC-specific prefixes like "ecdsa-with-" and RSA-specific prefixes like "withrsaencryption", directly resolving the KeyError: 'ecdsa-with-SHA384' reported in issue #118.


365-369: LGTM! Appropriate serialization formats chosen.

Using PKCS8 for EC keys (modern standard) and TraditionalOpenSSL for RSA keys (backward compatible) is the right approach for maintaining compatibility while supporting new key types.


307-326: EC curve mappings are correct.

The key length to curve mappings align with the cryptography library's standard NIST/SECG curves:

  • 224 → ec.SECP224R1 (P-224)
  • 256 → ec.SECP256R1 (P-256)
  • 384 → ec.SECP384R1 (P-384)
  • 521 → ec.SECP521R1 (P-521)

The implementation correctly instantiates and passes these curves to ec.generate_private_key(), following the cryptography library's standard usage pattern.

Improved error message clarity and added validation of unsupported keys.

Fixes #118
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @django_x509/base/models.py:
- Around line 317-329: Remove support for the 224-bit EC curve by deleting the
"224": ec.SECP224R1() entry from the curves mapping in the EC key generation
branch (the dict used in the elif self.key_type == "ec" block) and update the
EC_KEY_LENGTHS constant to remove "224" so it lists only ("256", "384", "521");
ensure no other references expect "224" in the same module (e.g., validation
logic that uses EC_KEY_LENGTHS) are left unchanged except for that removal.
🧹 Nitpick comments (5)
django_x509/base/models.py (5)

21-30: Consider grouping or labeling key length choices by type.

The combined KEY_LENGTH_CHOICES now mixes EC (224, 256, 384, 521) and RSA (512, 1024, 2048, 4096) lengths without visual separation. Users selecting a length in the admin or forms may not immediately know which lengths apply to their chosen key type until validation fails.

Potential UX improvement

Consider one of these approaches:

  1. Add visual grouping in forms using optgroup (requires custom widget)
  2. Add descriptive labels like ("224", "224 (EC)") and ("2048", "2048 (RSA)")
  3. Dynamically filter choices based on key_type in forms

Example for approach 2:

 KEY_LENGTH_CHOICES = (
-    ("224", "224"),
-    ("256", "256"),
-    ("384", "384"),
-    ("521", "521"),
-    ("512", "512"),
-    ("1024", "1024"),
-    ("2048", "2048"),
-    ("4096", "4096"),
+    ("224", "224 (EC)"),
+    ("256", "256 (EC)"),
+    ("384", "384 (EC)"),
+    ("521", "521 (EC)"),
+    ("512", "512 (RSA)"),
+    ("1024", "1024 (RSA)"),
+    ("2048", "2048 (RSA)"),
+    ("4096", "4096 (RSA)"),
 )

196-209: Consider validating that key_length is in the appropriate set.

The current validation correctly prevents mismatched key_type/key_length combinations (e.g., RSA with EC lengths). However, it doesn't validate that the key_length is actually valid for ANY key type.

More comprehensive validation
+        # Validate key_length is in the valid set for the chosen key_type
+        if self.key_type == "rsa":
+            if self.key_length not in RSA_KEY_LENGTHS:
+                raise ValidationError(
+                    {
+                        "key_length": _(
+                            "Invalid key length for RSA. "
+                            "Valid lengths are: %s"
+                        ) % ", ".join(RSA_KEY_LENGTHS)
+                    }
+                )
+        elif self.key_type == "ec":
+            if self.key_length not in EC_KEY_LENGTHS:
+                raise ValidationError(
+                    {
+                        "key_length": _(
+                            "Invalid key length for Elliptic Curve. "
+                            "Valid lengths are: %s"
+                        ) % ", ".join(EC_KEY_LENGTHS)
+                    }
+                )
-        if self.key_type == "rsa" and self.key_length in EC_KEY_LENGTHS:
-            raise ValidationError(
-                {
-                    "key_length": _(
-                        "Selected length is only valid for Elliptic Curve keys. "
-                        "RSA keys must use 512, 1024, 2048, or 4096 bits."
-                    )
-                }
-            )
-
-        if self.key_type == "ec" and self.key_length in RSA_KEY_LENGTHS:
-            raise ValidationError(
-                {"key_length": _("Selected length is not valid for Elliptic Curve.")}
-            )

This approach validates positively (is the value in the valid set?) rather than negatively (is the value in the wrong set?), which is more robust.


318-323: Consider extracting curve mapping to a module-level constant.

The curves dictionary is recreated on every certificate generation. While the performance impact is negligible (certificate generation is infrequent), extracting it improves maintainability and keeps related constants together.

Refactor suggestion

At module level, after the existing constants:

EC_CURVES = {
    "224": ec.SECP224R1,
    "256": ec.SECP256R1,
    "384": ec.SECP384R1,
    "521": ec.SECP521R1,
}

Then in _generate:

         elif self.key_type == "ec":
-            curves = {
-                "224": ec.SECP224R1(),
-                "256": ec.SECP256R1(),
-                "384": ec.SECP384R1(),
-                "521": ec.SECP521R1(),
-            }
-            curve = curves.get(self.key_length)
+            curve_class = EC_CURVES.get(self.key_length)
-            if not curve:
+            if not curve_class:
                 raise ValidationError(
                     _("Unsupported EC key length: %s") % self.key_length
                 )
-            key = ec.generate_private_key(curve)
+            key = ec.generate_private_key(curve_class())

Note: Store the class, not the instance, to avoid unnecessary instantiation at module import.


408-421: Import may accept certificates with unsupported EC curves.

The import logic correctly detects RSA vs EC keys and extracts the key size. However, when importing a certificate with an EC curve not in the supported set (e.g., SECP192R1, SECP256K1), the validation error from clean() will say "Selected length is not valid for Elliptic Curve," which is misleading—the issue is the curve, not the length value.

Improve error messaging for unsupported curves

Add curve validation immediately after detection:

         elif isinstance(public_key, ec.EllipticCurvePublicKey):
             self.key_type = "ec"
             self.key_length = str(public_key.curve.key_size)
+            if self.key_length not in EC_KEY_LENGTHS:
+                raise ValidationError(
+                    _(
+                        "Unsupported EC curve in certificate. "
+                        "Supported curves: SECP224R1 (224-bit), SECP256R1 (256-bit), "
+                        "SECP384R1 (384-bit), SECP521R1 (521-bit)."
+                    )
+                )
         else:

This provides immediate, clear feedback during import rather than a confusing validation error later.


37-37: 512-bit RSA support is intentional and used for certificate compatibility.

While 512-bit RSA is indeed cryptographically weak by modern standards, its inclusion in RSA_KEY_LENGTHS appears deliberate. The model's validation logic explicitly lists it as a valid option, and the test suite actively verifies that 512-bit certificates can be imported and parsed. This suggests the library is designed to handle legacy X.509 certificates rather than restrict key generation.

If the project's scope includes supporting certificate parsing and import of existing infrastructure, retaining 512-bit support may be necessary. However, if the intention is to restrict new certificate generation, consider:

  • Adding a separate constant for SUPPORTED_RSA_KEY_LENGTHS (for parsing) vs GENERATED_RSA_KEY_LENGTHS (for new keys)
  • Documenting the rationale for 512-bit support in a code comment
  • Adding a deprecation warning when 512-bit certificates are used
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2f3be8 and 86a6b9f.

📒 Files selected for processing (1)
  • django_x509/base/models.py
🔇 Additional comments (4)
django_x509/base/models.py (4)

163-165: LGTM!

The key_type field is well-defined with an appropriate default of "rsa" for backward compatibility.


370-374: LGTM! Private key format selection is appropriate.

Using PKCS8 for EC keys is correct and necessary, as the TraditionalOpenSSL format only supports RSA. Maintaining TraditionalOpenSSL for RSA keys preserves backward compatibility with existing certificates.


436-436: No changes needed. The digest field is consistently stored as a simple hash algorithm name (e.g., "sha256", "sha1") during both import and generation. The signature_hash_algorithm.name property from the cryptography library returns only the hash name without signature scheme prefixes, so the import at line 436 already stores normalized values that match the DIGEST_CHOICES field options. The normalization code in _generate() (lines 355-359) is defensive but unnecessary since those prefixes are never present in practice.

Likely an incorrect or invalid review comment.


355-359: The digest normalization may be unnecessary — verify it actually solves the reported issue.

The signature_hash_algorithm.name property from Python's cryptography library returns only the hash algorithm name (e.g., "sha256", "sha1"), not the full signature algorithm identifier. The code attempts to strip RSA and EC prefixes ("withrsaencryption", "ecdsa-with-"), but these should not appear in the digest value based on how the certificate is parsed. If issue #118 involved a KeyError with the digest, the root cause may be elsewhere. Please confirm this normalization is actually needed by verifying the exact error from that issue.

Removed 224-bit curve support as modern standards recommend 256-bit curves (SECP256R1/P-256) as the minimum.

Fixes #118
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86a6b9f and 678566d.

📒 Files selected for processing (3)
  • django_x509/base/models.py
  • django_x509/migrations/0012_alter_ca_key_length_alter_cert_key_length.py
  • tests/openwisp2/sample_x509/migrations/0005_alter_ca_key_length_alter_cert_key_length_and_more.py
🧰 Additional context used
🧬 Code graph analysis (2)
tests/openwisp2/sample_x509/migrations/0005_alter_ca_key_length_alter_cert_key_length_and_more.py (2)
django_x509/migrations/0012_alter_ca_key_length_alter_cert_key_length.py (1)
  • Migration (7-52)
django_x509/base/models.py (1)
  • default_key_length (80-85)
django_x509/migrations/0012_alter_ca_key_length_alter_cert_key_length.py (2)
tests/openwisp2/sample_x509/migrations/0005_alter_ca_key_length_alter_cert_key_length_and_more.py (1)
  • Migration (7-71)
django_x509/base/models.py (1)
  • default_key_length (80-85)
🪛 Ruff (0.14.10)
tests/openwisp2/sample_x509/migrations/0005_alter_ca_key_length_alter_cert_key_length_and_more.py

9-11: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


13-71: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

django_x509/migrations/0012_alter_ca_key_length_alter_cert_key_length.py

9-11: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


13-52: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

🔇 Additional comments (8)
django_x509/migrations/0012_alter_ca_key_length_alter_cert_key_length.py (1)

1-52: LGTM!

The migration correctly adds EC key length choices (256, 384, 521) alongside existing RSA key lengths. The structure is consistent with the sample_x509 migration and follows Django migration conventions.

Note: The Ruff RUF012 warnings about ClassVar annotations are false positives—Django migrations intentionally use mutable class attributes as part of their design pattern.

tests/openwisp2/sample_x509/migrations/0005_alter_ca_key_length_alter_cert_key_length_and_more.py (1)

1-71: LGTM!

The test migration is consistent with the main django_x509 migration (0012), applying the same key_length choices across all three models (CA, Cert, CustomCert). The Ruff warnings are false positives for Django migrations.

django_x509/base/models.py (6)

21-37: LGTM!

The key length choices and type constants are well-structured. The EC key lengths (256, 384, 521) correctly correspond to SECP256R1 (P-256), SECP384R1 (P-384), and SECP521R1 (P-521) curves.


162-164: LGTM!

The key_type field is correctly defined with appropriate max_length=3 (sufficient for "rsa" and "ec"), and the default of "rsa" maintains backward compatibility with existing certificates.


195-208: LGTM!

The cross-field validation correctly prevents mismatched key type and key length combinations, with clear error messages that guide users to valid choices.


311-329: LGTM!

The key generation logic correctly handles both RSA and EC key types with proper curve mapping. The defensive error handling for unsupported key lengths and types provides good resilience even though clean() validation should catch these cases earlier.


353-358: LGTM!

The digest name normalization correctly handles both RSA signature algorithm names (e.g., sha256WithRSAEncryptionsha256) and ECDSA signature algorithm names (e.g., ecdsa-with-sha384sha384), ensuring the hash algorithm lookup succeeds.


366-374: LGTM!

Using PKCS8 format for EC keys and TraditionalOpenSSL for RSA keys is a sensible choice—PKCS8 is the recommended format for EC keys while TraditionalOpenSSL maintains backward compatibility for RSA keys.

…with unsupported key lengths #118

Added explicit validation in _import() to reject unsupported EC curves with a clear, user-friendly error message.

Fixes #118
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
django_x509/base/models.py (2)

369-373: Consider using PKCS8 format for both RSA and EC keys.

The current implementation uses TraditionalOpenSSL for RSA (preserving backward compatibility) and PKCS8 for EC. While this works, using PKCS8 for both key types would simplify the codebase and align with modern standards.

If backward compatibility with existing private keys is not a concern, consider this simplification:

♻️ Unified PKCS8 format
 self.private_key = key.private_bytes(
     encoding=serialization.Encoding.PEM,
-    format=(
-        serialization.PrivateFormat.PKCS8
-        if self.key_type == "ec"
-        else serialization.PrivateFormat.TraditionalOpenSSL
-    ),
+    format=serialization.PrivateFormat.PKCS8,
     encryption_algorithm=encryption,
 ).decode("utf-8")

353-358: The digest normalization is unnecessary for current cryptography usage.

The signature_hash_algorithm.name from the cryptography library already returns lowercase hash names (e.g., "sha256", "sha384") in the format expected by HASH_MAP. The .replace() calls for "withrsaencryption", "ecdsa-with-", and "withsha384" handle formats that cryptography never produces. This defensive code is harmless but can be simplified by directly using the digest value from imported certificates, removing the unnecessary string replacements.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 678566d and cf82aca.

📒 Files selected for processing (1)
  • django_x509/base/models.py
🔇 Additional comments (7)
django_x509/base/models.py (7)

21-29: LGTM! EC key lengths are correct.

The key lengths for EC curves are accurate. Note that "521" (not "512") is correct for the SECP521R1 curve—this is a well-known naming quirk in NIST elliptic curves.


31-37: LGTM! Key type choices and constants are well-defined.

The separation of RSA and EC key lengths into distinct constants enables clean validation logic throughout the codebase.


162-164: LGTM! Default ensures backward compatibility.

The key_type field defaults to "rsa", preserving existing behavior for records created before this feature.


195-208: LGTM! Validation prevents key type/length mismatches.

The symmetric validation ensures users cannot select incompatible key_type and key_length combinations, addressing a key objective from the PR.


407-423: LGTM! Import logic correctly detects and validates key types.

The import path now properly handles both RSA and EC certificates, resolving the KeyError from issue #118. The validation ensures only supported EC curves (P-256, P-384, P-521) can be imported, providing clear error messages for unsupported curves.


495-507: LGTM! CA verification supports both RSA and EC signatures.

The _verify_ca() method correctly handles signature verification for both RSA (using PKCS1v15 padding) and EC (using ECDSA) public keys, ensuring imported EC certificates can be properly validated against their CA.


311-329: EC curve support is confirmed in cryptography 46.0.3.

The EC key generation logic correctly maps key lengths to NIST curves (SECP256R1, SECP384R1, SECP521R1) with appropriate error handling for unsupported lengths and key types. The implementation is correct.

Added a test to verify full edcsa lifecycle with respect to the changes.

Fixes #118
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @django_x509/tests/test_ca.py:
- Around line 725-732: Add a call to full_clean() on the Ca instance before
save() in the test: when creating gen_ca via Ca(name=f"Generated-EC-{length}",
key_type="ec", key_length=length) call gen_ca.full_clean() prior to
gen_ca.save() so the test mirrors the import test’s validation pattern; keep the
existing assertions on gen_ca.pkey, original_cert/ gen_ca.certificate after
gen_ca.renew(), and key_type intact.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf82aca and c6e4de0.

📒 Files selected for processing (1)
  • django_x509/tests/test_ca.py
🔇 Additional comments (1)
django_x509/tests/test_ca.py (1)

4-7: Imports look appropriate for the new EC lifecycle test.
No concerns with adding hashes, serialization, and ec here.

… ECDSA signature variants #118

Updated test implementation to now pair curves with their standard matching digests and use datetime.now(dt_timezone.utc) instead of timezone.now() for the certificate validity dates.

Fixes #118
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
django_x509/tests/test_ca.py (1)

689-737: Excellent EC lifecycle test with good coverage!

The test effectively covers EC CA import, generation, and renewal flows with appropriate curve/digest pairings. The use of subTest for parameterization is a best practice.

Optional: Consider adding a few more assertions for completeness

While the test provides solid coverage, you could enhance it with:

  1. Verify imported CA key type (after line 726):
self.assertIsInstance(ca.pkey, ec.EllipticCurvePrivateKey)
  1. Verify renewal preserves key metadata (after line 737):
gen_ca.refresh_from_db()
self.assertEqual(gen_ca.key_type, "ec")
self.assertEqual(gen_ca.key_length, length)
self.assertNotEqual(gen_ca.private_key, original_cert)  # Verify key changed

These additions would align with the patterns in test_renew() (lines 624-656) and test_pkey_property() (lines 107-109).

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c6e4de0 and f74fa1d.

📒 Files selected for processing (1)
  • django_x509/tests/test_ca.py
🔇 Additional comments (1)
django_x509/tests/test_ca.py (1)

5-6: LGTM! Necessary imports for EC key operations.

The added imports (hashes, serialization, ec) are appropriately used in the new test_ecdsa_full_lifecycle test method for EC key generation, certificate creation, and serialization.

Added assertions to verify imported CA key type and verify renewal
preserves key metadata.

Fixes #118
Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

Great work @stktyagi, I did a quick scan, see a few comments below.
I'll take some time in the next few days to perform manual testing.



Verifies Cert ECDSA import, generation, and renewal issued by an ECDSA CA.

Fixes #118
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@django_x509/tests/test_ca.py`:
- Around line 736-742: The test saves the original certificate to original_cert
but then incorrectly compares it to gen_ca.private_key; instead capture the
original private key before calling gen_ca.renew() (e.g., store
gen_ca.private_key into a variable like original_key), then after
gen_ca.refresh_from_db() assert that gen_ca.private_key != original_key to
verify the key changed, and keep the existing assertion that gen_ca.certificate
!= original_cert to verify the certificate changed.
🧹 Nitpick comments (1)
django_x509/base/models.py (1)

352-357: Incomplete digest normalization pattern.

The normalization handles withsha384 specifically but misses other potential patterns like withsha256 or withsha512. Consider a more general approach.

♻️ Suggested fix for more robust normalization
         digest_name = (
             self.digest.lower()
             .replace("withrsaencryption", "")
             .replace("ecdsa-with-", "")
-            .replace("withsha384", "sha384")
+            .replace("withsha", "sha")
         )

This handles withsha256, withsha384, withsha512, etc. uniformly.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 00b6542 and 1a5604f.

📒 Files selected for processing (3)
  • django_x509/base/models.py
  • django_x509/tests/test_ca.py
  • django_x509/tests/test_cert.py
🧰 Additional context used
🧬 Code graph analysis (2)
django_x509/tests/test_cert.py (3)
django_x509/models.py (2)
  • Ca (6-13)
  • Cert (16-23)
tests/openwisp2/sample_x509/models.py (2)
  • Ca (13-19)
  • Cert (22-28)
django_x509/base/models.py (5)
  • save (209-215)
  • x509 (218-223)
  • pkey (248-257)
  • renew (666-673)
  • renew (699-709)
django_x509/tests/test_ca.py (2)
django_x509/base/models.py (5)
  • x509 (218-223)
  • save (209-215)
  • pkey (248-257)
  • renew (666-673)
  • renew (699-709)
tests/openwisp2/sample_x509/models.py (1)
  • Ca (13-19)
🔇 Additional comments (10)
django_x509/base/models.py (6)

21-37: LGTM! Well-structured key type and length constants.

The separation of RSA and EC key lengths into distinct tuples enables clear validation. The removal of 224-bit and inclusion of 521-bit aligns with modern security recommendations.


162-164: LGTM! Field definition is appropriate.

The key_type field with max_length=3, choices constraint, and "rsa" default maintains backward compatibility with existing certificates.


195-207: LGTM! Comprehensive cross-field validation.

The bidirectional validation between key_type and key_length prevents invalid combinations with clear, actionable error messages.


310-328: LGTM! Correct EC key generation implementation.

The curve mapping matches EC_KEY_LENGTHS and uses the appropriate NIST curves (SECP256R1, SECP384R1, SECP521R1). Error handling for unsupported lengths and key types is properly included.


368-372: LGTM! Appropriate private key format selection.

Using PKCS8 for EC keys is the standard choice, while TraditionalOpenSSL for RSA maintains backward compatibility with existing systems and certificates.


406-422: LGTM! Robust import handling for different key types.

The implementation correctly:

  • Detects and sets appropriate key_type and key_length for RSA and EC keys
  • Validates EC curve sizes against supported lengths
  • Rejects unsupported key types (DSA, Ed25519, etc.) with a clear error message
django_x509/tests/test_ca.py (2)

5-6: LGTM!

Necessary imports for ECDSA certificate operations in the new test.


689-735: LGTM! Comprehensive ECDSA CA lifecycle test.

The test properly covers:

  • All three supported EC curves with curve-appropriate hash algorithms
  • Import flow with externally generated EC certificates
  • Generation flow with model-based EC CA creation
  • Proper use of subTest for clear test isolation and reporting
django_x509/tests/test_cert.py (2)

5-6: LGTM!

Necessary imports for ECDSA certificate operations in the new test.


495-550: LGTM! Comprehensive ECDSA Cert lifecycle test.

The test properly covers:

  • All three supported EC curves with curve-appropriate hash algorithms
  • Creation of EC CAs for issuing certificates
  • Import flow with externally generated EC certificates signed by the CA
  • Generation flow with model-based EC Cert creation
  • Renewal verification ensuring key_type is preserved
  • Proper use of subTest for clear test isolation

This addresses the reviewer request from past comments to add a corresponding test in test_cert.py.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

…118

Fixed inconsistent assertion by storing and comparing the original private key.

Fixes #118
@nemesifier
Copy link
Member

@coderabbitai resume

@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

✅ Actions performed

Reviews resumed.

Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

@stktyagi what do you think about this?

🧹 Nitpick comments (1)

django_x509/base/models.py (1)> 352-357: Incomplete digest normalization pattern.

The normalization handles withsha384 specifically but misses other potential patterns like withsha256 or withsha512. Consider a more general approach.

♻️ Suggested fix for more robust normalization

         digest_name = (
             self.digest.lower()
             .replace("withrsaencryption", "")
             .replace("ecdsa-with-", "")
-            .replace("withsha384", "sha384")
+            .replace("withsha", "sha")
         )

This handles withsha256, withsha384, withsha512, etc. uniformly.

@stktyagi
Copy link
Member Author

@stktyagi what do you think about this?

Totally valid, I might have missed this. I'll update this right away.

Updated digest pattern to more generalized approach to handle every pattern.

Fixes #118
coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 14, 2026
@github-project-automation github-project-automation bot moved this from To do (general) to In progress in OpenWISP Contributor's Board Jan 14, 2026
Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

@stktyagi have you tested this in the admin at all? I don't think so. The feature is not available at all from the admin.

The README also will need to be updated to reflect these changes.

See also my comments below.

@stktyagi
Copy link
Member Author

@stktyagi have you tested this in the admin at all? I don't think so. The feature is not available at all from the admin.

The README also will need to be updated to reflect these changes.

See also my comments below.

@nemesifier The original error was about not being able to import ecdsa certs and I was able to manually test it in the earlier commits but as the commits went by and we went to full fledged ecdsa support I didnt keep up with the testing. I'll make sure going ahead.

@nemesifier
Copy link
Member

@stktyagi have you tested this in the admin at all? I don't think so. The feature is not available at all from the admin.
The README also will need to be updated to reflect these changes.
See also my comments below.

@nemesifier The original error was about not being able to import ecdsa certs and I was able to manually test it in the earlier commits but as the commits went by and we went to full fledged ecdsa support I didnt keep up with the testing. I'll make sure going ahead.

@stktyagi sounds good! We'll need to test how OpenWISP Controller interacts with this version and ensure its admin and REST API continue working fine.

Implemented ECDSA support using internal logic to automatically infer the algorithm from the key_length choice, ensuring full backward compatibility without adding new database fields or migrations.

Fixes #118
coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 16, 2026
Add migrations files related to changes made to the KEY_LENGTH_CHOICES

Fixes #118
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@django_x509/migrations/0010_alter_ca_key_length_alter_cert_key_length.py`:
- Around line 18-26: The migration's choices arrays in
django_x509.migrations.0010_alter_ca_key_length_alter_cert_key_length.py are
missing the "224 (ECDSA)" option; update both the CA key length choices and the
cert key length choices (the two choices= [...] lists shown) to include ("224",
"224 (ECDSA)") in the appropriate position so the migration matches the model's
allowed EC key sizes and prevents drift.

In
`@tests/openwisp2/sample_x509/migrations/0003_alter_ca_key_length_alter_cert_key_length_and_more.py`:
- Around line 18-26: The three choices arrays for key length in the migration
(for the fields ca_key_length, cert_key_length, and customcert_key_length) are
missing the 224 ECDSA option; add the tuple ("224", "224 (ECDSA)") into each
choices list (preferably placed before ("256", "256 (ECDSA)") to keep numeric
order) so the AlterField choice definitions in
0003_alter_ca_key_length_alter_cert_key_length_and_more.py match the newly
supported EC key sizes.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3eaa43 and 94b0793.

📒 Files selected for processing (2)
  • django_x509/migrations/0010_alter_ca_key_length_alter_cert_key_length.py
  • tests/openwisp2/sample_x509/migrations/0003_alter_ca_key_length_alter_cert_key_length_and_more.py
🧰 Additional context used
🧬 Code graph analysis (2)
django_x509/migrations/0010_alter_ca_key_length_alter_cert_key_length.py (2)
tests/openwisp2/sample_x509/migrations/0003_alter_ca_key_length_alter_cert_key_length_and_more.py (1)
  • Migration (7-71)
django_x509/base/models.py (1)
  • default_key_length (75-80)
tests/openwisp2/sample_x509/migrations/0003_alter_ca_key_length_alter_cert_key_length_and_more.py (2)
django_x509/migrations/0010_alter_ca_key_length_alter_cert_key_length.py (1)
  • Migration (7-52)
django_x509/base/models.py (1)
  • default_key_length (75-80)
🪛 Ruff (0.14.13)
django_x509/migrations/0010_alter_ca_key_length_alter_cert_key_length.py

9-11: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


13-52: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

tests/openwisp2/sample_x509/migrations/0003_alter_ca_key_length_alter_cert_key_length_and_more.py

9-11: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


13-71: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 16, 2026
Modified clean() to check for unsupported key lengths and removed previous complex mismatch checks

Fixes #118
coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 16, 2026
Remove 512-bit RSA support from models and refactor test data by replacing 512-bit certificates with 2048-bit RSA

Fixes #118
@stktyagi stktyagi requested a review from nemesifier January 18, 2026 09:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

[feature] Importing existing CA: KeyError: 'ecdsa-with-SHA384'

4 participants