Skip to content

Commit

Permalink
fixes #11920 raise a clean Python error on DSA signing failure due to…
Browse files Browse the repository at this point in the history
… nilpotent (#11921)
  • Loading branch information
alex authored Nov 11, 2024
1 parent d251c8a commit da437d1
Show file tree
Hide file tree
Showing 9 changed files with 52 additions and 3 deletions.
4 changes: 4 additions & 0 deletions docs/development/test-vectors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ Custom asymmetric vectors
encrypted at the PEM level with AES-128-CBC and password "a123456".
* ``asymmetric/DER_Serialization/testrsa.der`` - The above as a DER-encoded
RSAPrivateKey structure.
* ``asymmetric/DSA/custom/nilpotent.pem`` -- A key where the field is actually
a ring and the generator of the multiplicative subgroup is actually
nilpotent with low degree. Taken from BoringSSL (see
``TEST(DSATest, NilpotentGenerator)``).


Key exchange
Expand Down
1 change: 1 addition & 0 deletions docs/spelling_wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ namespace
namespaces
macOS
naïve
nilpotent
Nonces
nonces
online
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ __all__ = [
CRYPTOGRAPHY_IS_LIBRESSL: bool
CRYPTOGRAPHY_IS_BORINGSSL: bool
CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: bool
CRYPTOGRAPHY_OPENSSL_309_OR_GREATER: bool
CRYPTOGRAPHY_OPENSSL_320_OR_GREATER: bool

class Providers: ...
Expand Down
2 changes: 1 addition & 1 deletion src/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ name = "cryptography_rust"
crate-type = ["cdylib"]

[lints.rust]
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"))'] }
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"))'] }
3 changes: 3 additions & 0 deletions src/rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ fn main() {
if version >= 0x3_00_00_00_0 {
println!("cargo:rustc-cfg=CRYPTOGRAPHY_OPENSSL_300_OR_GREATER");
}
if version >= 0x3_00_09_00_0 {
println!("cargo:rustc-cfg=CRYPTOGRAPHY_OPENSSL_309_OR_GREATER");
}
if version >= 0x3_02_00_00_0 {
println!("cargo:rustc-cfg=CRYPTOGRAPHY_OPENSSL_320_OR_GREATER");
}
Expand Down
10 changes: 8 additions & 2 deletions src/rust/src/backend/dsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
use crate::backend::utils;
use crate::buf::CffiBuf;
use crate::error::{CryptographyError, CryptographyResult};
use crate::exceptions;
use crate::{error, exceptions};
use pyo3::types::PyAnyMethods;
use pyo3::ToPyObject;

#[pyo3::pyclass(
frozen,
Expand Down Expand Up @@ -76,7 +77,12 @@ impl DsaPrivateKey {
let mut signer = openssl::pkey_ctx::PkeyCtx::new(&self.pkey)?;
signer.sign_init()?;
let mut sig = vec![];
signer.sign_to_vec(data.as_bytes(), &mut sig)?;
signer.sign_to_vec(data.as_bytes(), &mut sig).map_err(|e| {
pyo3::exceptions::PyValueError::new_err((
"DSA signing failed. This generally indicates an invalid key.",
error::list_from_openssl_error(py, &e).to_object(py),
))
})?;
Ok(pyo3::types::PyBytes::new_bound(py, &sig))
}

Expand Down
4 changes: 4 additions & 0 deletions src/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ mod _rust {
"CRYPTOGRAPHY_OPENSSL_300_OR_GREATER",
cfg!(CRYPTOGRAPHY_OPENSSL_300_OR_GREATER),
)?;
openssl_mod.add(
"CRYPTOGRAPHY_OPENSSL_309_OR_GREATER",
cfg!(CRYPTOGRAPHY_OPENSSL_309_OR_GREATER),
)?;
openssl_mod.add(
"CRYPTOGRAPHY_OPENSSL_320_OR_GREATER",
cfg!(CRYPTOGRAPHY_OPENSSL_320_OR_GREATER),
Expand Down
25 changes: 25 additions & 0 deletions tests/hazmat/primitives/test_dsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from cryptography import utils
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa
from cryptography.hazmat.primitives.asymmetric.utils import (
Expand Down Expand Up @@ -550,6 +551,30 @@ def test_prehashed_digest_mismatch(self, backend):
with pytest.raises(ValueError):
private_key.sign(digest, prehashed_alg)

@pytest.mark.supported(
only_if=lambda _: (
rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL
or rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL
or rust_openssl.CRYPTOGRAPHY_OPENSSL_309_OR_GREATER
),
skip_message="Requires OpenSSL 3.0.9+, LibreSSL, or BoringSSL",
)
def test_nilpotent(self):
try:
key = load_vectors_from_file(
os.path.join("asymmetric", "DSA", "custom", "nilpotent.pem"),
lambda pemfile: serialization.load_pem_private_key(
pemfile.read().encode(), password=None
),
)
except ValueError:
# LibreSSL simply rejects this key on load.
return
assert isinstance(key, dsa.DSAPrivateKey)

with pytest.raises(ValueError):
key.sign(b"anything", hashes.SHA256())


class TestDSANumbers:
def test_dsa_parameter_numbers(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN DSA PRIVATE KEY-----
MGECAQACFQHH+MnFXh4NNlZiV/zUVb5a5ib3kwIVAOP8ZOKvDwabKzEr/moq3y1z
E3vJAhUAl/2Ylx9fWbzHdh1URsc/c6IM/TECAQECFCsjU4AZRcuks45g1NMOUeCB
Epvg
-----END DSA PRIVATE KEY-----

0 comments on commit da437d1

Please sign in to comment.