From bc613a584e5d71d82821f789cc05c936c5231d21 Mon Sep 17 00:00:00 2001 From: "Sean B. Palmer" Date: Mon, 27 Apr 2026 17:27:15 +0100 Subject: [PATCH] Expose DSA bits in key parameters --- src/info.rs | 10 ++++++++++ src/openpgp/_openpgp.pyi | 2 ++ tests/test_key_generation.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/src/info.rs b/src/info.rs index f997d80..1d27066 100644 --- a/src/info.rs +++ b/src/info.rs @@ -238,6 +238,7 @@ pub(crate) fn empty_public_params_info(kind: &str) -> PublicParamsInfo { curve_oid: None, curve_alias: None, curve_bits: None, + dsa_bits: None, rsa_bits: None, secret_key_length: None, is_supported: None, @@ -263,6 +264,9 @@ pub(crate) fn public_params_info_from_params(params: &PgpPublicParams) -> Public PgpPublicParams::RSA(params) => { info.rsa_bits = u32::try_from(params.key.n().bits()).ok(); } + PgpPublicParams::DSA(params) => { + info.dsa_bits = u32::try_from(params.key.components().p().bits()).ok(); + } PgpPublicParams::ECDSA(params) => match params { PgpEcdsaPublicParams::P256 { .. } => { set_curve_metadata(&mut info, &ECCCurve::P256); @@ -927,6 +931,7 @@ pub(crate) struct PublicParamsInfo { pub(crate) curve_oid: Option, pub(crate) curve_alias: Option, pub(crate) curve_bits: Option, + pub(crate) dsa_bits: Option, pub(crate) rsa_bits: Option, pub(crate) secret_key_length: Option, pub(crate) is_supported: Option, @@ -967,6 +972,11 @@ impl PublicParamsInfo { self.curve_bits } + #[getter] + fn dsa_bits(&self) -> Option { + self.dsa_bits + } + /// The encoded RSA modulus size in bits, when this key uses RSA public parameters. #[getter] fn rsa_bits(&self) -> Option { diff --git a/src/openpgp/_openpgp.pyi b/src/openpgp/_openpgp.pyi index b1dfa77..9521118 100644 --- a/src/openpgp/_openpgp.pyi +++ b/src/openpgp/_openpgp.pyi @@ -260,6 +260,8 @@ class PublicParamsInfo: @property def curve_bits(self) -> Optional[int]: ... @property + def dsa_bits(self) -> Optional[int]: ... + @property def rsa_bits(self) -> Optional[int]: ... @property def secret_key_length(self) -> Optional[int]: ... diff --git a/tests/test_key_generation.py b/tests/test_key_generation.py index 8a2b97a..dbd58ac 100644 --- a/tests/test_key_generation.py +++ b/tests/test_key_generation.py @@ -379,6 +379,34 @@ def test_legacy_curve25519_public_params_expose_curve_metadata() -> None: assert reparsed_public.subkey_bindings()[0].public_params.curve == "curve25519" +def test_dsa_public_params_expose_prime_size_for_generated_and_parsed_keys() -> None: + secret_key = ( + SecretKeyParamsBuilder() + .created_at(FIXED_PRIMARY_CREATED_AT) + .key_type(KeyType.dsa(1024)) + .can_certify(True) + .can_sign(True) + .primary_user_id("alice") + .build() + .generate() + ) + public_key = secret_key.to_public_key() + + for key in (secret_key, public_key): + params = key.public_params + assert params.kind == "dsa" + assert params.dsa_bits == 1024 + assert params.rsa_bits is None + assert params.curve is None + assert params.curve_bits is None + assert params.secret_key_length is None + + reparsed_secret, _ = SecretKey.from_armor(secret_key.to_armored()) + reparsed_public, _ = PublicKey.from_armor(public_key.to_armored()) + assert reparsed_secret.public_params.dsa_bits == 1024 + assert reparsed_public.public_params.dsa_bits == 1024 + + def test_rsa_public_params_expose_modulus_size_for_generated_and_parsed_keys() -> None: secret_key = ( SecretKeyParamsBuilder() @@ -396,6 +424,7 @@ def test_rsa_public_params_expose_modulus_size_for_generated_and_parsed_keys() - params = key.public_params assert params.kind == "rsa" assert params.rsa_bits == 2048 + assert params.dsa_bits is None assert params.curve is None assert params.curve_bits is None assert params.secret_key_length is None