Skip to content

Commit da437d1

Browse files
authored
fixes pyca#11920 raise a clean Python error on DSA signing failure due to nilpotent (pyca#11921)
1 parent d251c8a commit da437d1

File tree

9 files changed

+52
-3
lines changed

9 files changed

+52
-3
lines changed

docs/development/test-vectors.rst

+4
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,10 @@ Custom asymmetric vectors
183183
encrypted at the PEM level with AES-128-CBC and password "a123456".
184184
* ``asymmetric/DER_Serialization/testrsa.der`` - The above as a DER-encoded
185185
RSAPrivateKey structure.
186+
* ``asymmetric/DSA/custom/nilpotent.pem`` -- A key where the field is actually
187+
a ring and the generator of the multiplicative subgroup is actually
188+
nilpotent with low degree. Taken from BoringSSL (see
189+
``TEST(DSATest, NilpotentGenerator)``).
186190

187191

188192
Key exchange

docs/spelling_wordlist.txt

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ namespace
8989
namespaces
9090
macOS
9191
naïve
92+
nilpotent
9293
Nonces
9394
nonces
9495
online

src/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ __all__ = [
4848
CRYPTOGRAPHY_IS_LIBRESSL: bool
4949
CRYPTOGRAPHY_IS_BORINGSSL: bool
5050
CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: bool
51+
CRYPTOGRAPHY_OPENSSL_309_OR_GREATER: bool
5152
CRYPTOGRAPHY_OPENSSL_320_OR_GREATER: bool
5253

5354
class Providers: ...

src/rust/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ name = "cryptography_rust"
3232
crate-type = ["cdylib"]
3333

3434
[lints.rust]
35-
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(CRYPTOGRAPHY_OPENSSL_300_OR_GREATER)', 'cfg(CRYPTOGRAPHY_OPENSSL_320_OR_GREATER)', 'cfg(CRYPTOGRAPHY_IS_LIBRESSL)', 'cfg(CRYPTOGRAPHY_IS_BORINGSSL)', 'cfg(CRYPTOGRAPHY_OSSLCONF, values("OPENSSL_NO_IDEA", "OPENSSL_NO_CAST", "OPENSSL_NO_BF", "OPENSSL_NO_CAMELLIA", "OPENSSL_NO_SEED", "OPENSSL_NO_SM4"))'] }
35+
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(CRYPTOGRAPHY_OPENSSL_300_OR_GREATER)', 'cfg(CRYPTOGRAPHY_OPENSSL_309_OR_GREATER)', 'cfg(CRYPTOGRAPHY_OPENSSL_320_OR_GREATER)', 'cfg(CRYPTOGRAPHY_IS_LIBRESSL)', 'cfg(CRYPTOGRAPHY_IS_BORINGSSL)', 'cfg(CRYPTOGRAPHY_OSSLCONF, values("OPENSSL_NO_IDEA", "OPENSSL_NO_CAST", "OPENSSL_NO_BF", "OPENSSL_NO_CAMELLIA", "OPENSSL_NO_SEED", "OPENSSL_NO_SM4"))'] }

src/rust/build.rs

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ fn main() {
1212
if version >= 0x3_00_00_00_0 {
1313
println!("cargo:rustc-cfg=CRYPTOGRAPHY_OPENSSL_300_OR_GREATER");
1414
}
15+
if version >= 0x3_00_09_00_0 {
16+
println!("cargo:rustc-cfg=CRYPTOGRAPHY_OPENSSL_309_OR_GREATER");
17+
}
1518
if version >= 0x3_02_00_00_0 {
1619
println!("cargo:rustc-cfg=CRYPTOGRAPHY_OPENSSL_320_OR_GREATER");
1720
}

src/rust/src/backend/dsa.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
use crate::backend::utils;
66
use crate::buf::CffiBuf;
77
use crate::error::{CryptographyError, CryptographyResult};
8-
use crate::exceptions;
8+
use crate::{error, exceptions};
99
use pyo3::types::PyAnyMethods;
10+
use pyo3::ToPyObject;
1011

1112
#[pyo3::pyclass(
1213
frozen,
@@ -76,7 +77,12 @@ impl DsaPrivateKey {
7677
let mut signer = openssl::pkey_ctx::PkeyCtx::new(&self.pkey)?;
7778
signer.sign_init()?;
7879
let mut sig = vec![];
79-
signer.sign_to_vec(data.as_bytes(), &mut sig)?;
80+
signer.sign_to_vec(data.as_bytes(), &mut sig).map_err(|e| {
81+
pyo3::exceptions::PyValueError::new_err((
82+
"DSA signing failed. This generally indicates an invalid key.",
83+
error::list_from_openssl_error(py, &e).to_object(py),
84+
))
85+
})?;
8086
Ok(pyo3::types::PyBytes::new_bound(py, &sig))
8187
}
8288

src/rust/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@ mod _rust {
199199
"CRYPTOGRAPHY_OPENSSL_300_OR_GREATER",
200200
cfg!(CRYPTOGRAPHY_OPENSSL_300_OR_GREATER),
201201
)?;
202+
openssl_mod.add(
203+
"CRYPTOGRAPHY_OPENSSL_309_OR_GREATER",
204+
cfg!(CRYPTOGRAPHY_OPENSSL_309_OR_GREATER),
205+
)?;
202206
openssl_mod.add(
203207
"CRYPTOGRAPHY_OPENSSL_320_OR_GREATER",
204208
cfg!(CRYPTOGRAPHY_OPENSSL_320_OR_GREATER),

tests/hazmat/primitives/test_dsa.py

+25
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from cryptography import utils
1414
from cryptography.exceptions import InvalidSignature
15+
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
1516
from cryptography.hazmat.primitives import hashes, serialization
1617
from cryptography.hazmat.primitives.asymmetric import dsa
1718
from cryptography.hazmat.primitives.asymmetric.utils import (
@@ -550,6 +551,30 @@ def test_prehashed_digest_mismatch(self, backend):
550551
with pytest.raises(ValueError):
551552
private_key.sign(digest, prehashed_alg)
552553

554+
@pytest.mark.supported(
555+
only_if=lambda _: (
556+
rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL
557+
or rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
558+
or rust_openssl.CRYPTOGRAPHY_OPENSSL_309_OR_GREATER
559+
),
560+
skip_message="Requires OpenSSL 3.0.9+, LibreSSL, or BoringSSL",
561+
)
562+
def test_nilpotent(self):
563+
try:
564+
key = load_vectors_from_file(
565+
os.path.join("asymmetric", "DSA", "custom", "nilpotent.pem"),
566+
lambda pemfile: serialization.load_pem_private_key(
567+
pemfile.read().encode(), password=None
568+
),
569+
)
570+
except ValueError:
571+
# LibreSSL simply rejects this key on load.
572+
return
573+
assert isinstance(key, dsa.DSAPrivateKey)
574+
575+
with pytest.raises(ValueError):
576+
key.sign(b"anything", hashes.SHA256())
577+
553578

554579
class TestDSANumbers:
555580
def test_dsa_parameter_numbers(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN DSA PRIVATE KEY-----
2+
MGECAQACFQHH+MnFXh4NNlZiV/zUVb5a5ib3kwIVAOP8ZOKvDwabKzEr/moq3y1z
3+
E3vJAhUAl/2Ylx9fWbzHdh1URsc/c6IM/TECAQECFCsjU4AZRcuks45g1NMOUeCB
4+
Epvg
5+
-----END DSA PRIVATE KEY-----

0 commit comments

Comments
 (0)