From 91e41898e6d1d2a9a6e980c39e2f8baa2fa8a1f8 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Mon, 29 May 2023 22:32:12 -0400 Subject: [PATCH] Port DSA to Rust (#8978) --- src/_cffi_src/openssl/bignum.py | 2 - src/_cffi_src/openssl/dsa.py | 3 - src/_cffi_src/openssl/evp.py | 1 - .../hazmat/backends/openssl/backend.py | 117 +----- .../hazmat/backends/openssl/dsa.py | 246 ------------- .../bindings/_rust/openssl/__init__.pyi | 2 + .../hazmat/bindings/_rust/openssl/dsa.pyi | 20 ++ .../hazmat/primitives/asymmetric/dsa.py | 4 + src/rust/src/backend/dh.rs | 3 +- src/rust/src/backend/dsa.rs | 333 ++++++++++++++++++ src/rust/src/backend/ed25519.rs | 3 +- src/rust/src/backend/ed448.rs | 3 +- src/rust/src/backend/mod.rs | 2 + src/rust/src/backend/utils.rs | 40 ++- src/rust/src/backend/x25519.rs | 3 +- src/rust/src/backend/x448.rs | 3 +- src/rust/src/error.rs | 7 +- tests/hazmat/primitives/test_dsa.py | 6 + tests/hazmat/primitives/test_x25519.py | 7 + tests/x509/test_x509_crlbuilder.py | 4 + 20 files changed, 445 insertions(+), 364 deletions(-) delete mode 100644 src/cryptography/hazmat/backends/openssl/dsa.py create mode 100644 src/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi create mode 100644 src/rust/src/backend/dsa.rs diff --git a/src/_cffi_src/openssl/bignum.py b/src/_cffi_src/openssl/bignum.py index 999e10cd031b..044403325582 100644 --- a/src/_cffi_src/openssl/bignum.py +++ b/src/_cffi_src/openssl/bignum.py @@ -39,8 +39,6 @@ int BN_MONT_CTX_set(BN_MONT_CTX *, const BIGNUM *, BN_CTX *); void BN_MONT_CTX_free(BN_MONT_CTX *); -BIGNUM *BN_dup(const BIGNUM *); - int BN_set_word(BIGNUM *, BN_ULONG); char *BN_bn2hex(const BIGNUM *); diff --git a/src/_cffi_src/openssl/dsa.py b/src/_cffi_src/openssl/dsa.py index 04478a0e577b..d91076393582 100644 --- a/src/_cffi_src/openssl/dsa.py +++ b/src/_cffi_src/openssl/dsa.py @@ -23,10 +23,7 @@ int DSA_verify(int, const unsigned char *, int, const unsigned char *, int, DSA *); -void DSA_get0_pqg(const DSA *, const BIGNUM **, const BIGNUM **, - const BIGNUM **); int DSA_set0_pqg(DSA *, BIGNUM *, BIGNUM *, BIGNUM *); -void DSA_get0_key(const DSA *, const BIGNUM **, const BIGNUM **); int DSA_set0_key(DSA *, BIGNUM *, BIGNUM *); int DSA_generate_parameters_ex(DSA *, int, unsigned char *, int, int *, unsigned long *, BN_GENCB *); diff --git a/src/_cffi_src/openssl/evp.py b/src/_cffi_src/openssl/evp.py index d5875f7d09b0..ce54fd9fe931 100644 --- a/src/_cffi_src/openssl/evp.py +++ b/src/_cffi_src/openssl/evp.py @@ -66,7 +66,6 @@ int EVP_PKEY_type(int); int EVP_PKEY_size(EVP_PKEY *); RSA *EVP_PKEY_get1_RSA(EVP_PKEY *); -DSA *EVP_PKEY_get1_DSA(EVP_PKEY *); int EVP_PKEY_encrypt(EVP_PKEY_CTX *, unsigned char *, size_t *, const unsigned char *, size_t); diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index f2e381a15d61..02d51094cfe5 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -15,11 +15,6 @@ from cryptography.hazmat.backends.openssl import aead from cryptography.hazmat.backends.openssl.ciphers import _CipherContext from cryptography.hazmat.backends.openssl.cmac import _CMACContext -from cryptography.hazmat.backends.openssl.dsa import ( - _DSAParameters, - _DSAPrivateKey, - _DSAPublicKey, -) from cryptography.hazmat.backends.openssl.ec import ( _EllipticCurvePrivateKey, _EllipticCurvePublicKey, @@ -551,10 +546,9 @@ def _evp_pkey_to_private_key( unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation, ) elif key_type == self._lib.EVP_PKEY_DSA: - dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) - self.openssl_assert(dsa_cdata != self._ffi.NULL) - dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - return _DSAPrivateKey(self, dsa_cdata, evp_pkey) + return rust_openssl.dsa.private_key_from_ptr( + int(self._ffi.cast("uintptr_t", evp_pkey)) + ) elif key_type == self._lib.EVP_PKEY_EC: ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) self.openssl_assert(ec_cdata != self._ffi.NULL) @@ -613,10 +607,9 @@ def _evp_pkey_to_public_key(self, evp_pkey) -> PublicKeyTypes: self.openssl_assert(res == 1) return self.load_der_public_key(self._read_mem_bio(bio)) elif key_type == self._lib.EVP_PKEY_DSA: - dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) - self.openssl_assert(dsa_cdata != self._ffi.NULL) - dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - return _DSAPublicKey(self, dsa_cdata, evp_pkey) + return rust_openssl.dsa.public_key_from_ptr( + int(self._ffi.cast("uintptr_t", evp_pkey)) + ) elif key_type == self._lib.EVP_PKEY_EC: ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) if ec_cdata == self._ffi.NULL: @@ -696,36 +689,12 @@ def generate_dsa_parameters(self, key_size: int) -> dsa.DSAParameters: "Key size must be 1024, 2048, 3072, or 4096 bits." ) - ctx = self._lib.DSA_new() - self.openssl_assert(ctx != self._ffi.NULL) - ctx = self._ffi.gc(ctx, self._lib.DSA_free) - - res = self._lib.DSA_generate_parameters_ex( - ctx, - key_size, - self._ffi.NULL, - 0, - self._ffi.NULL, - self._ffi.NULL, - self._ffi.NULL, - ) - - self.openssl_assert(res == 1) - - return _DSAParameters(self, ctx) + return rust_openssl.dsa.generate_parameters(key_size) def generate_dsa_private_key( self, parameters: dsa.DSAParameters ) -> dsa.DSAPrivateKey: - ctx = self._lib.DSAparams_dup( - parameters._dsa_cdata # type: ignore[attr-defined] - ) - self.openssl_assert(ctx != self._ffi.NULL) - ctx = self._ffi.gc(ctx, self._lib.DSA_free) - self._lib.DSA_generate_key(ctx) - evp_pkey = self._dsa_cdata_to_evp_pkey(ctx) - - return _DSAPrivateKey(self, ctx, evp_pkey) + return parameters.generate_private_key() def generate_dsa_private_key_and_parameters( self, key_size: int @@ -733,78 +702,28 @@ def generate_dsa_private_key_and_parameters( parameters = self.generate_dsa_parameters(key_size) return self.generate_dsa_private_key(parameters) - def _dsa_cdata_set_values( - self, dsa_cdata, p, q, g, pub_key, priv_key - ) -> None: - res = self._lib.DSA_set0_pqg(dsa_cdata, p, q, g) - self.openssl_assert(res == 1) - res = self._lib.DSA_set0_key(dsa_cdata, pub_key, priv_key) - self.openssl_assert(res == 1) - def load_dsa_private_numbers( self, numbers: dsa.DSAPrivateNumbers ) -> dsa.DSAPrivateKey: dsa._check_dsa_private_numbers(numbers) - parameter_numbers = numbers.public_numbers.parameter_numbers - - dsa_cdata = self._lib.DSA_new() - self.openssl_assert(dsa_cdata != self._ffi.NULL) - dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - - p = self._int_to_bn(parameter_numbers.p) - q = self._int_to_bn(parameter_numbers.q) - g = self._int_to_bn(parameter_numbers.g) - pub_key = self._int_to_bn(numbers.public_numbers.y) - priv_key = self._int_to_bn(numbers.x) - self._dsa_cdata_set_values(dsa_cdata, p, q, g, pub_key, priv_key) - - evp_pkey = self._dsa_cdata_to_evp_pkey(dsa_cdata) - - return _DSAPrivateKey(self, dsa_cdata, evp_pkey) + return rust_openssl.dsa.from_private_numbers(numbers) def load_dsa_public_numbers( self, numbers: dsa.DSAPublicNumbers ) -> dsa.DSAPublicKey: dsa._check_dsa_parameters(numbers.parameter_numbers) - dsa_cdata = self._lib.DSA_new() - self.openssl_assert(dsa_cdata != self._ffi.NULL) - dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - - p = self._int_to_bn(numbers.parameter_numbers.p) - q = self._int_to_bn(numbers.parameter_numbers.q) - g = self._int_to_bn(numbers.parameter_numbers.g) - pub_key = self._int_to_bn(numbers.y) - priv_key = self._ffi.NULL - self._dsa_cdata_set_values(dsa_cdata, p, q, g, pub_key, priv_key) - - evp_pkey = self._dsa_cdata_to_evp_pkey(dsa_cdata) - - return _DSAPublicKey(self, dsa_cdata, evp_pkey) + return rust_openssl.dsa.from_public_numbers(numbers) def load_dsa_parameter_numbers( self, numbers: dsa.DSAParameterNumbers ) -> dsa.DSAParameters: dsa._check_dsa_parameters(numbers) - dsa_cdata = self._lib.DSA_new() - self.openssl_assert(dsa_cdata != self._ffi.NULL) - dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - - p = self._int_to_bn(numbers.p) - q = self._int_to_bn(numbers.q) - g = self._int_to_bn(numbers.g) - res = self._lib.DSA_set0_pqg(dsa_cdata, p, q, g) - self.openssl_assert(res == 1) - - return _DSAParameters(self, dsa_cdata) - - def _dsa_cdata_to_evp_pkey(self, dsa_cdata): - evp_pkey = self._create_evp_pkey_gc() - res = self._lib.EVP_PKEY_set1_DSA(evp_pkey, dsa_cdata) - self.openssl_assert(res == 1) - return evp_pkey + return rust_openssl.dsa.from_parameter_numbers(numbers) def dsa_supported(self) -> bool: - return not self._fips_enabled + return ( + not self._lib.CRYPTOGRAPHY_IS_BORINGSSL and not self._fips_enabled + ) def dsa_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: if not self.dsa_supported(): @@ -1409,8 +1328,6 @@ def _private_key_bytes( if encoding is serialization.Encoding.PEM: if key_type == self._lib.EVP_PKEY_RSA: write_bio = self._lib.PEM_write_bio_RSAPrivateKey - elif key_type == self._lib.EVP_PKEY_DSA: - write_bio = self._lib.PEM_write_bio_DSAPrivateKey else: assert key_type == self._lib.EVP_PKEY_EC write_bio = self._lib.PEM_write_bio_ECPrivateKey @@ -1426,11 +1343,9 @@ def _private_key_bytes( ) if key_type == self._lib.EVP_PKEY_RSA: write_bio = self._lib.i2d_RSAPrivateKey_bio - elif key_type == self._lib.EVP_PKEY_EC: - write_bio = self._lib.i2d_ECPrivateKey_bio else: - assert key_type == self._lib.EVP_PKEY_DSA - write_bio = self._lib.i2d_DSAPrivateKey_bio + assert key_type == self._lib.EVP_PKEY_EC + write_bio = self._lib.i2d_ECPrivateKey_bio return self._bio_func_output(write_bio, cdata) raise ValueError("Unsupported encoding for TraditionalOpenSSL") diff --git a/src/cryptography/hazmat/backends/openssl/dsa.py b/src/cryptography/hazmat/backends/openssl/dsa.py deleted file mode 100644 index 411a80820e85..000000000000 --- a/src/cryptography/hazmat/backends/openssl/dsa.py +++ /dev/null @@ -1,246 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.backends.openssl.utils import ( - _calculate_digest_and_algorithm, -) -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import dsa -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - - -def _dsa_sig_sign( - backend: Backend, private_key: _DSAPrivateKey, data: bytes -) -> bytes: - sig_buf_len = backend._lib.DSA_size(private_key._dsa_cdata) - sig_buf = backend._ffi.new("unsigned char[]", sig_buf_len) - buflen = backend._ffi.new("unsigned int *") - - # The first parameter passed to DSA_sign is unused by OpenSSL but - # must be an integer. - res = backend._lib.DSA_sign( - 0, data, len(data), sig_buf, buflen, private_key._dsa_cdata - ) - backend.openssl_assert(res == 1) - backend.openssl_assert(buflen[0]) - - return backend._ffi.buffer(sig_buf)[: buflen[0]] - - -def _dsa_sig_verify( - backend: Backend, - public_key: _DSAPublicKey, - signature: bytes, - data: bytes, -) -> None: - # The first parameter passed to DSA_verify is unused by OpenSSL but - # must be an integer. - res = backend._lib.DSA_verify( - 0, data, len(data), signature, len(signature), public_key._dsa_cdata - ) - - if res != 1: - backend._consume_errors() - raise InvalidSignature - - -class _DSAParameters(dsa.DSAParameters): - def __init__(self, backend: Backend, dsa_cdata): - self._backend = backend - self._dsa_cdata = dsa_cdata - - def parameter_numbers(self) -> dsa.DSAParameterNumbers: - p = self._backend._ffi.new("BIGNUM **") - q = self._backend._ffi.new("BIGNUM **") - g = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DSA_get0_pqg(self._dsa_cdata, p, q, g) - self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(q[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) - return dsa.DSAParameterNumbers( - p=self._backend._bn_to_int(p[0]), - q=self._backend._bn_to_int(q[0]), - g=self._backend._bn_to_int(g[0]), - ) - - def generate_private_key(self) -> dsa.DSAPrivateKey: - return self._backend.generate_dsa_private_key(self) - - -class _DSAPrivateKey(dsa.DSAPrivateKey): - _key_size: int - - def __init__(self, backend: Backend, dsa_cdata, evp_pkey): - self._backend = backend - self._dsa_cdata = dsa_cdata - self._evp_pkey = evp_pkey - - p = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DSA_get0_pqg( - dsa_cdata, p, self._backend._ffi.NULL, self._backend._ffi.NULL - ) - self._backend.openssl_assert(p[0] != backend._ffi.NULL) - self._key_size = self._backend._lib.BN_num_bits(p[0]) - - @property - def key_size(self) -> int: - return self._key_size - - def private_numbers(self) -> dsa.DSAPrivateNumbers: - p = self._backend._ffi.new("BIGNUM **") - q = self._backend._ffi.new("BIGNUM **") - g = self._backend._ffi.new("BIGNUM **") - pub_key = self._backend._ffi.new("BIGNUM **") - priv_key = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DSA_get0_pqg(self._dsa_cdata, p, q, g) - self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(q[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) - self._backend._lib.DSA_get0_key(self._dsa_cdata, pub_key, priv_key) - self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(priv_key[0] != self._backend._ffi.NULL) - return dsa.DSAPrivateNumbers( - public_numbers=dsa.DSAPublicNumbers( - parameter_numbers=dsa.DSAParameterNumbers( - p=self._backend._bn_to_int(p[0]), - q=self._backend._bn_to_int(q[0]), - g=self._backend._bn_to_int(g[0]), - ), - y=self._backend._bn_to_int(pub_key[0]), - ), - x=self._backend._bn_to_int(priv_key[0]), - ) - - def public_key(self) -> dsa.DSAPublicKey: - dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata) - self._backend.openssl_assert(dsa_cdata != self._backend._ffi.NULL) - dsa_cdata = self._backend._ffi.gc( - dsa_cdata, self._backend._lib.DSA_free - ) - pub_key = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DSA_get0_key( - self._dsa_cdata, pub_key, self._backend._ffi.NULL - ) - self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) - pub_key_dup = self._backend._lib.BN_dup(pub_key[0]) - res = self._backend._lib.DSA_set0_key( - dsa_cdata, pub_key_dup, self._backend._ffi.NULL - ) - self._backend.openssl_assert(res == 1) - evp_pkey = self._backend._dsa_cdata_to_evp_pkey(dsa_cdata) - return _DSAPublicKey(self._backend, dsa_cdata, evp_pkey) - - def parameters(self) -> dsa.DSAParameters: - dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata) - self._backend.openssl_assert(dsa_cdata != self._backend._ffi.NULL) - dsa_cdata = self._backend._ffi.gc( - dsa_cdata, self._backend._lib.DSA_free - ) - return _DSAParameters(self._backend, dsa_cdata) - - def private_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PrivateFormat, - encryption_algorithm: serialization.KeySerializationEncryption, - ) -> bytes: - return self._backend._private_key_bytes( - encoding, - format, - encryption_algorithm, - self, - self._evp_pkey, - self._dsa_cdata, - ) - - def sign( - self, - data: bytes, - algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], - ) -> bytes: - data, _ = _calculate_digest_and_algorithm(data, algorithm) - return _dsa_sig_sign(self._backend, self, data) - - -class _DSAPublicKey(dsa.DSAPublicKey): - _key_size: int - - def __init__(self, backend: Backend, dsa_cdata, evp_pkey): - self._backend = backend - self._dsa_cdata = dsa_cdata - self._evp_pkey = evp_pkey - p = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DSA_get0_pqg( - dsa_cdata, p, self._backend._ffi.NULL, self._backend._ffi.NULL - ) - self._backend.openssl_assert(p[0] != backend._ffi.NULL) - self._key_size = self._backend._lib.BN_num_bits(p[0]) - - @property - def key_size(self) -> int: - return self._key_size - - def __eq__(self, other: object) -> bool: - if not isinstance(other, _DSAPublicKey): - return NotImplemented - - return ( - self._backend._lib.EVP_PKEY_cmp(self._evp_pkey, other._evp_pkey) - == 1 - ) - - def public_numbers(self) -> dsa.DSAPublicNumbers: - p = self._backend._ffi.new("BIGNUM **") - q = self._backend._ffi.new("BIGNUM **") - g = self._backend._ffi.new("BIGNUM **") - pub_key = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DSA_get0_pqg(self._dsa_cdata, p, q, g) - self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(q[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) - self._backend._lib.DSA_get0_key( - self._dsa_cdata, pub_key, self._backend._ffi.NULL - ) - self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) - return dsa.DSAPublicNumbers( - parameter_numbers=dsa.DSAParameterNumbers( - p=self._backend._bn_to_int(p[0]), - q=self._backend._bn_to_int(q[0]), - g=self._backend._bn_to_int(g[0]), - ), - y=self._backend._bn_to_int(pub_key[0]), - ) - - def parameters(self) -> dsa.DSAParameters: - dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata) - dsa_cdata = self._backend._ffi.gc( - dsa_cdata, self._backend._lib.DSA_free - ) - return _DSAParameters(self._backend, dsa_cdata) - - def public_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PublicFormat, - ) -> bytes: - return self._backend._public_key_bytes( - encoding, format, self, self._evp_pkey, None - ) - - def verify( - self, - signature: bytes, - data: bytes, - algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], - ) -> None: - data, _ = _calculate_digest_and_algorithm(data, algorithm) - return _dsa_sig_verify(self._backend, self, signature, data) diff --git a/src/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi b/src/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi index 9ab4e6c98cd6..82f30d20b0ab 100644 --- a/src/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi +++ b/src/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi @@ -6,6 +6,7 @@ import typing from cryptography.hazmat.bindings._rust.openssl import ( dh, + dsa, ed448, ed25519, hashes, @@ -20,6 +21,7 @@ __all__ = [ "openssl_version", "raise_openssl_error", "dh", + "dsa", "hashes", "hmac", "kdf", diff --git a/src/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi b/src/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi new file mode 100644 index 000000000000..5a56f256d52d --- /dev/null +++ b/src/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi @@ -0,0 +1,20 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat.primitives.asymmetric import dsa + +class DSAPrivateKey: ... +class DSAPublicKey: ... +class DSAParameters: ... + +def generate_parameters(key_size: int) -> dsa.DSAParameters: ... +def private_key_from_ptr(ptr: int) -> dsa.DSAPrivateKey: ... +def public_key_from_ptr(ptr: int) -> dsa.DSAPublicKey: ... +def from_private_numbers( + numbers: dsa.DSAPrivateNumbers, +) -> dsa.DSAPrivateKey: ... +def from_public_numbers(numbers: dsa.DSAPublicNumbers) -> dsa.DSAPublicKey: ... +def from_parameter_numbers( + numbers: dsa.DSAParameterNumbers, +) -> dsa.DSAParameters: ... diff --git a/src/cryptography/hazmat/primitives/asymmetric/dsa.py b/src/cryptography/hazmat/primitives/asymmetric/dsa.py index 1ebfcd52ad13..a8c52de4fb49 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/dsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -7,6 +7,7 @@ import abc import typing +from cryptography.hazmat.bindings._rust import openssl as rust_openssl from cryptography.hazmat.primitives import _serialization, hashes from cryptography.hazmat.primitives.asymmetric import utils as asym_utils @@ -26,6 +27,7 @@ def parameter_numbers(self) -> DSAParameterNumbers: DSAParametersWithNumbers = DSAParameters +DSAParameters.register(rust_openssl.dsa.DSAParameters) class DSAPrivateKey(metaclass=abc.ABCMeta): @@ -77,6 +79,7 @@ def private_bytes( DSAPrivateKeyWithSerialization = DSAPrivateKey +DSAPrivateKey.register(rust_openssl.dsa.DSAPrivateKey) class DSAPublicKey(metaclass=abc.ABCMeta): @@ -128,6 +131,7 @@ def __eq__(self, other: object) -> bool: DSAPublicKeyWithSerialization = DSAPublicKey +DSAPublicKey.register(rust_openssl.dsa.DSAPublicKey) class DSAParameterNumbers: diff --git a/src/rust/src/backend/dh.rs b/src/rust/src/backend/dh.rs index b4dbaf5dded5..7f523c09e594 100644 --- a/src/rust/src/backend/dh.rs +++ b/src/rust/src/backend/dh.rs @@ -271,6 +271,7 @@ impl DHPrivateKey { format, encryption_algorithm, true, + false, ) } } @@ -302,7 +303,7 @@ impl DHPublicKey { )); } - utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, true) + utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, true, false) } fn parameters(&self) -> CryptographyResult { diff --git a/src/rust/src/backend/dsa.rs b/src/rust/src/backend/dsa.rs new file mode 100644 index 000000000000..59a5a676d5d5 --- /dev/null +++ b/src/rust/src/backend/dsa.rs @@ -0,0 +1,333 @@ +// This file is dual licensed under the terms of the Apache License, Version +// 2.0, and the BSD License. See the LICENSE file in the root of this repository +// for complete details. + +use crate::backend::utils; +use crate::error::{CryptographyError, CryptographyResult}; +use crate::exceptions; +use foreign_types_shared::ForeignTypeRef; + +#[pyo3::prelude::pyclass( + module = "cryptography.hazmat.bindings._rust.openssl.dsa", + name = "DSAPrivateKey" +)] +struct DsaPrivateKey { + pkey: openssl::pkey::PKey, +} + +#[pyo3::prelude::pyclass( + module = "cryptography.hazmat.bindings._rust.openssl.dsa", + name = "DSAPublicKey" +)] +struct DsaPublicKey { + pkey: openssl::pkey::PKey, +} + +#[pyo3::prelude::pyclass( + module = "cryptography.hazmat.bindings._rust.openssl.dsa", + name = "DSAParameters" +)] +struct DsaParameters { + dsa: openssl::dsa::Dsa, +} + +#[pyo3::prelude::pyfunction] +fn private_key_from_ptr(ptr: usize) -> DsaPrivateKey { + let pkey = unsafe { openssl::pkey::PKeyRef::from_ptr(ptr as *mut _) }; + DsaPrivateKey { + pkey: pkey.to_owned(), + } +} + +#[pyo3::prelude::pyfunction] +fn public_key_from_ptr(ptr: usize) -> DsaPublicKey { + let pkey = unsafe { openssl::pkey::PKeyRef::from_ptr(ptr as *mut _) }; + DsaPublicKey { + pkey: pkey.to_owned(), + } +} + +#[pyo3::prelude::pyfunction] +fn generate_parameters(key_size: u32) -> CryptographyResult { + let dsa = openssl::dsa::Dsa::generate_params(key_size)?; + Ok(DsaParameters { dsa }) +} + +#[pyo3::prelude::pyfunction] +fn from_private_numbers( + py: pyo3::Python<'_>, + numbers: &pyo3::PyAny, +) -> CryptographyResult { + let public_numbers = numbers.getattr(pyo3::intern!(py, "public_numbers"))?; + let parameter_numbers = public_numbers.getattr(pyo3::intern!(py, "parameter_numbers"))?; + + let dsa = openssl::dsa::Dsa::from_private_components( + utils::py_int_to_bn(py, parameter_numbers.getattr(pyo3::intern!(py, "p"))?)?, + utils::py_int_to_bn(py, parameter_numbers.getattr(pyo3::intern!(py, "q"))?)?, + utils::py_int_to_bn(py, parameter_numbers.getattr(pyo3::intern!(py, "g"))?)?, + utils::py_int_to_bn(py, numbers.getattr(pyo3::intern!(py, "x"))?)?, + utils::py_int_to_bn(py, public_numbers.getattr(pyo3::intern!(py, "y"))?)?, + ) + .unwrap(); + let pkey = openssl::pkey::PKey::from_dsa(dsa)?; + Ok(DsaPrivateKey { pkey }) +} + +#[pyo3::prelude::pyfunction] +fn from_public_numbers( + py: pyo3::Python<'_>, + numbers: &pyo3::PyAny, +) -> CryptographyResult { + let parameter_numbers = numbers.getattr(pyo3::intern!(py, "parameter_numbers"))?; + + let dsa = openssl::dsa::Dsa::from_public_components( + utils::py_int_to_bn(py, parameter_numbers.getattr(pyo3::intern!(py, "p"))?)?, + utils::py_int_to_bn(py, parameter_numbers.getattr(pyo3::intern!(py, "q"))?)?, + utils::py_int_to_bn(py, parameter_numbers.getattr(pyo3::intern!(py, "g"))?)?, + utils::py_int_to_bn(py, numbers.getattr(pyo3::intern!(py, "y"))?)?, + ) + .unwrap(); + let pkey = openssl::pkey::PKey::from_dsa(dsa)?; + Ok(DsaPublicKey { pkey }) +} + +#[pyo3::prelude::pyfunction] +fn from_parameter_numbers( + py: pyo3::Python<'_>, + numbers: &pyo3::PyAny, +) -> CryptographyResult { + let dsa = openssl::dsa::Dsa::from_pqg( + utils::py_int_to_bn(py, numbers.getattr(pyo3::intern!(py, "p"))?)?, + utils::py_int_to_bn(py, numbers.getattr(pyo3::intern!(py, "q"))?)?, + utils::py_int_to_bn(py, numbers.getattr(pyo3::intern!(py, "g"))?)?, + ) + .unwrap(); + Ok(DsaParameters { dsa }) +} + +fn clone_dsa_params( + d: &openssl::dsa::Dsa, +) -> Result, openssl::error::ErrorStack> { + openssl::dsa::Dsa::from_pqg(d.p().to_owned()?, d.q().to_owned()?, d.g().to_owned()?) +} + +#[pyo3::prelude::pymethods] +impl DsaPrivateKey { + fn sign<'p>( + &self, + py: pyo3::Python<'p>, + data: &pyo3::types::PyBytes, + algorithm: &pyo3::PyAny, + ) -> CryptographyResult<&'p pyo3::types::PyBytes> { + let (data, _): (&[u8], &pyo3::PyAny) = py + .import(pyo3::intern!( + py, + "cryptography.hazmat.backends.openssl.utils" + ))? + .call_method1( + pyo3::intern!(py, "_calculate_digest_and_algorithm"), + (data, algorithm), + )? + .extract()?; + + let mut signer = openssl::pkey_ctx::PkeyCtx::new(&self.pkey)?; + signer.sign_init()?; + let mut sig = vec![]; + signer.sign_to_vec(data, &mut sig)?; + Ok(pyo3::types::PyBytes::new(py, &sig)) + } + + #[getter] + fn key_size(&self) -> i32 { + self.pkey.dsa().unwrap().p().num_bits() + } + + fn public_key(&self) -> CryptographyResult { + let priv_dsa = self.pkey.dsa()?; + let pub_dsa = openssl::dsa::Dsa::from_public_components( + priv_dsa.p().to_owned()?, + priv_dsa.q().to_owned()?, + priv_dsa.g().to_owned()?, + priv_dsa.pub_key().to_owned()?, + ) + .unwrap(); + let pkey = openssl::pkey::PKey::from_dsa(pub_dsa)?; + Ok(DsaPublicKey { pkey }) + } + + fn parameters(&self) -> CryptographyResult { + let dsa = clone_dsa_params(&self.pkey.dsa().unwrap())?; + Ok(DsaParameters { dsa }) + } + + fn private_numbers<'p>(&self, py: pyo3::Python<'p>) -> CryptographyResult<&'p pyo3::PyAny> { + let dsa = self.pkey.dsa().unwrap(); + + let py_p = utils::bn_to_py_int(py, dsa.p())?; + let py_q = utils::bn_to_py_int(py, dsa.q())?; + let py_g = utils::bn_to_py_int(py, dsa.g())?; + + let py_pub_key = utils::bn_to_py_int(py, dsa.pub_key())?; + let py_private_key = utils::bn_to_py_int(py, dsa.priv_key())?; + + let dsa_mod = py.import(pyo3::intern!( + py, + "cryptography.hazmat.primitives.asymmetric.dsa" + ))?; + + let parameter_numbers = + dsa_mod.call_method1(pyo3::intern!(py, "DSAParameterNumbers"), (py_p, py_q, py_g))?; + let public_numbers = dsa_mod.call_method1( + pyo3::intern!(py, "DSAPublicNumbers"), + (py_pub_key, parameter_numbers), + )?; + + Ok(dsa_mod.call_method1( + pyo3::intern!(py, "DSAPrivateNumbers"), + (py_private_key, public_numbers), + )?) + } + + fn private_bytes<'p>( + slf: &pyo3::PyCell, + py: pyo3::Python<'p>, + encoding: &pyo3::PyAny, + format: &pyo3::PyAny, + encryption_algorithm: &pyo3::PyAny, + ) -> CryptographyResult<&'p pyo3::types::PyBytes> { + utils::pkey_private_bytes( + py, + slf, + &slf.borrow().pkey, + encoding, + format, + encryption_algorithm, + true, + false, + ) + } +} + +#[pyo3::prelude::pymethods] +impl DsaPublicKey { + fn verify( + &self, + py: pyo3::Python<'_>, + signature: &[u8], + data: &pyo3::types::PyBytes, + algorithm: &pyo3::PyAny, + ) -> CryptographyResult<()> { + let (data, _): (&[u8], &pyo3::PyAny) = py + .import(pyo3::intern!( + py, + "cryptography.hazmat.backends.openssl.utils" + ))? + .call_method1( + pyo3::intern!(py, "_calculate_digest_and_algorithm"), + (data, algorithm), + )? + .extract()?; + + let mut verifier = openssl::pkey_ctx::PkeyCtx::new(&self.pkey)?; + verifier.verify_init()?; + let valid = verifier.verify(data, signature).unwrap_or(false); + if !valid { + return Err(CryptographyError::from( + exceptions::InvalidSignature::new_err(()), + )); + } + + Ok(()) + } + + #[getter] + fn key_size(&self) -> i32 { + self.pkey.dsa().unwrap().p().num_bits() + } + + fn parameters(&self) -> CryptographyResult { + let dsa = clone_dsa_params(&self.pkey.dsa().unwrap())?; + Ok(DsaParameters { dsa }) + } + + fn public_numbers<'p>(&self, py: pyo3::Python<'p>) -> CryptographyResult<&'p pyo3::PyAny> { + let dsa = self.pkey.dsa().unwrap(); + + let py_p = utils::bn_to_py_int(py, dsa.p())?; + let py_q = utils::bn_to_py_int(py, dsa.q())?; + let py_g = utils::bn_to_py_int(py, dsa.g())?; + + let py_pub_key = utils::bn_to_py_int(py, dsa.pub_key())?; + + let dsa_mod = py.import(pyo3::intern!( + py, + "cryptography.hazmat.primitives.asymmetric.dsa" + ))?; + + let parameter_numbers = + dsa_mod.call_method1(pyo3::intern!(py, "DSAParameterNumbers"), (py_p, py_q, py_g))?; + Ok(dsa_mod.call_method1( + pyo3::intern!(py, "DSAPublicNumbers"), + (py_pub_key, parameter_numbers), + )?) + } + + fn public_bytes<'p>( + slf: &pyo3::PyCell, + py: pyo3::Python<'p>, + encoding: &pyo3::PyAny, + format: &pyo3::PyAny, + ) -> CryptographyResult<&'p pyo3::types::PyBytes> { + utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, true, false) + } + + fn __richcmp__( + &self, + other: pyo3::PyRef<'_, DsaPublicKey>, + op: pyo3::basic::CompareOp, + ) -> pyo3::PyResult { + match op { + pyo3::basic::CompareOp::Eq => Ok(self.pkey.public_eq(&other.pkey)), + pyo3::basic::CompareOp::Ne => Ok(!self.pkey.public_eq(&other.pkey)), + _ => Err(pyo3::exceptions::PyTypeError::new_err("Cannot be ordered")), + } + } +} + +#[pyo3::prelude::pymethods] +impl DsaParameters { + fn generate_private_key(&self) -> CryptographyResult { + let dsa = clone_dsa_params(&self.dsa)?.generate_key()?; + let pkey = openssl::pkey::PKey::from_dsa(dsa)?; + Ok(DsaPrivateKey { pkey }) + } + + fn parameter_numbers<'p>(&self, py: pyo3::Python<'p>) -> CryptographyResult<&'p pyo3::PyAny> { + let py_p = utils::bn_to_py_int(py, self.dsa.p())?; + let py_q = utils::bn_to_py_int(py, self.dsa.q())?; + let py_g = utils::bn_to_py_int(py, self.dsa.g())?; + + let dsa_mod = py.import(pyo3::intern!( + py, + "cryptography.hazmat.primitives.asymmetric.dsa" + ))?; + + Ok(dsa_mod.call_method1(pyo3::intern!(py, "DSAParameterNumbers"), (py_p, py_q, py_g))?) + } +} + +pub(crate) fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&pyo3::prelude::PyModule> { + let m = pyo3::prelude::PyModule::new(py, "dsa")?; + m.add_function(pyo3::wrap_pyfunction!(private_key_from_ptr, m)?)?; + m.add_function(pyo3::wrap_pyfunction!(public_key_from_ptr, m)?)?; + m.add_function(pyo3::wrap_pyfunction!(generate_parameters, m)?)?; + m.add_function(pyo3::wrap_pyfunction!(from_private_numbers, m)?)?; + m.add_function(pyo3::wrap_pyfunction!(from_public_numbers, m)?)?; + m.add_function(pyo3::wrap_pyfunction!(from_parameter_numbers, m)?)?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + Ok(m) +} diff --git a/src/rust/src/backend/ed25519.rs b/src/rust/src/backend/ed25519.rs index 8cad193c7a92..7bee88104482 100644 --- a/src/rust/src/backend/ed25519.rs +++ b/src/rust/src/backend/ed25519.rs @@ -112,6 +112,7 @@ impl Ed25519PrivateKey { format, encryption_algorithm, true, + true, ) } } @@ -145,7 +146,7 @@ impl Ed25519PublicKey { encoding: &pyo3::PyAny, format: &pyo3::PyAny, ) -> CryptographyResult<&'p pyo3::types::PyBytes> { - utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, true) + utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, true, true) } fn __richcmp__( diff --git a/src/rust/src/backend/ed448.rs b/src/rust/src/backend/ed448.rs index 925a9fdb14f2..c0c621a321c3 100644 --- a/src/rust/src/backend/ed448.rs +++ b/src/rust/src/backend/ed448.rs @@ -110,6 +110,7 @@ impl Ed448PrivateKey { format, encryption_algorithm, true, + true, ) } } @@ -143,7 +144,7 @@ impl Ed448PublicKey { encoding: &pyo3::PyAny, format: &pyo3::PyAny, ) -> CryptographyResult<&'p pyo3::types::PyBytes> { - utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, true) + utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, true, true) } fn __richcmp__( diff --git a/src/rust/src/backend/mod.rs b/src/rust/src/backend/mod.rs index 970571193d15..765b0ab199f4 100644 --- a/src/rust/src/backend/mod.rs +++ b/src/rust/src/backend/mod.rs @@ -3,6 +3,7 @@ // for complete details. pub(crate) mod dh; +pub(crate) mod dsa; #[cfg(any(not(CRYPTOGRAPHY_IS_LIBRESSL), CRYPTOGRAPHY_LIBRESSL_370_OR_GREATER))] pub(crate) mod ed25519; #[cfg(all(not(CRYPTOGRAPHY_IS_LIBRESSL), not(CRYPTOGRAPHY_IS_BORINGSSL)))] @@ -19,6 +20,7 @@ pub(crate) mod x448; pub(crate) fn add_to_module(module: &pyo3::prelude::PyModule) -> pyo3::PyResult<()> { module.add_submodule(dh::create_module(module.py())?)?; + module.add_submodule(dsa::create_module(module.py())?)?; #[cfg(any(not(CRYPTOGRAPHY_IS_LIBRESSL), CRYPTOGRAPHY_LIBRESSL_370_OR_GREATER))] module.add_submodule(ed25519::create_module(module.py())?)?; diff --git a/src/rust/src/backend/utils.rs b/src/rust/src/backend/utils.rs index 072a80f5f73d..dea36117182b 100644 --- a/src/rust/src/backend/utils.rs +++ b/src/rust/src/backend/utils.rs @@ -37,6 +37,7 @@ pub(crate) fn bn_to_big_endian_bytes(b: &openssl::bn::BigNumRef) -> Cryptography Ok(b.to_vec_padded(b.num_bits() / 8 + 1)?) } +#[allow(clippy::too_many_arguments)] pub(crate) fn pkey_private_bytes<'p>( py: pyo3::Python<'p>, key_obj: &pyo3::PyAny, @@ -45,6 +46,7 @@ pub(crate) fn pkey_private_bytes<'p>( format: &pyo3::PyAny, encryption_algorithm: &pyo3::PyAny, openssh_allowed: bool, + raw_allowed: bool, ) -> CryptographyResult<&'p pyo3::types::PyBytes> { let serialization_mod = py.import(pyo3::intern!( py, @@ -89,8 +91,9 @@ pub(crate) fn pkey_private_bytes<'p>( } #[cfg(any(not(CRYPTOGRAPHY_IS_LIBRESSL), CRYPTOGRAPHY_LIBRESSL_370_OR_GREATER))] - if encoding.is(encoding_class.getattr(pyo3::intern!(py, "Raw"))?) - || format.is(private_format_class.getattr(pyo3::intern!(py, "Raw"))?) + if raw_allowed + && (encoding.is(encoding_class.getattr(pyo3::intern!(py, "Raw"))?) + || format.is(private_format_class.getattr(pyo3::intern!(py, "Raw"))?)) { if !encoding.is(encoding_class.getattr(pyo3::intern!(py, "Raw"))?) || !format.is(private_format_class.getattr(pyo3::intern!(py, "Raw"))?) @@ -151,6 +154,33 @@ pub(crate) fn pkey_private_bytes<'p>( )); } + if format.is(private_format_class.getattr(pyo3::intern!(py, "TraditionalOpenSSL"))?) { + if let Ok(dsa) = pkey.dsa() { + if encoding.is(encoding_class.getattr(pyo3::intern!(py, "PEM"))?) { + let pem_bytes = if password.is_empty() { + dsa.private_key_to_pem()? + } else { + dsa.private_key_to_pem_passphrase( + openssl::symm::Cipher::aes_256_cbc(), + password, + )? + }; + return Ok(pyo3::types::PyBytes::new(py, &pem_bytes)); + } else if encoding.is(encoding_class.getattr(pyo3::intern!(py, "DER"))?) { + if !password.is_empty() { + return Err(CryptographyError::from( + pyo3::exceptions::PyValueError::new_err( + "Encryption is not supported for DER encoded traditional OpenSSL keys", + ), + )); + } + + let der_bytes = dsa.private_key_to_der()?; + return Ok(pyo3::types::PyBytes::new(py, &der_bytes)); + } + } + } + // OpenSSH + PEM if openssh_allowed && format.is(private_format_class.getattr(pyo3::intern!(py, "OpenSSH"))?) { if encoding.is(encoding_class.getattr(pyo3::intern!(py, "PEM"))?) { @@ -185,6 +215,7 @@ pub(crate) fn pkey_public_bytes<'p>( encoding: &pyo3::PyAny, format: &pyo3::PyAny, openssh_allowed: bool, + raw_allowed: bool, ) -> CryptographyResult<&'p pyo3::types::PyBytes> { let serialization_mod = py.import(pyo3::intern!( py, @@ -213,8 +244,9 @@ pub(crate) fn pkey_public_bytes<'p>( } #[cfg(any(not(CRYPTOGRAPHY_IS_LIBRESSL), CRYPTOGRAPHY_LIBRESSL_370_OR_GREATER))] - if encoding.is(encoding_class.getattr(pyo3::intern!(py, "Raw"))?) - || format.is(public_format_class.getattr(pyo3::intern!(py, "Raw"))?) + if raw_allowed + && (encoding.is(encoding_class.getattr(pyo3::intern!(py, "Raw"))?) + || format.is(public_format_class.getattr(pyo3::intern!(py, "Raw"))?)) { if !encoding.is(encoding_class.getattr(pyo3::intern!(py, "Raw"))?) || !format.is(public_format_class.getattr(pyo3::intern!(py, "Raw"))?) diff --git a/src/rust/src/backend/x25519.rs b/src/rust/src/backend/x25519.rs index faf21ffddfe9..f27c0594ab3c 100644 --- a/src/rust/src/backend/x25519.rs +++ b/src/rust/src/backend/x25519.rs @@ -114,6 +114,7 @@ impl X25519PrivateKey { format, encryption_algorithm, false, + true, ) } } @@ -134,7 +135,7 @@ impl X25519PublicKey { encoding: &pyo3::PyAny, format: &pyo3::PyAny, ) -> CryptographyResult<&'p pyo3::types::PyBytes> { - utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, false) + utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, false, true) } fn __richcmp__( diff --git a/src/rust/src/backend/x448.rs b/src/rust/src/backend/x448.rs index 456e7fa52ab8..97e52ee6cc95 100644 --- a/src/rust/src/backend/x448.rs +++ b/src/rust/src/backend/x448.rs @@ -113,6 +113,7 @@ impl X448PrivateKey { format, encryption_algorithm, false, + true, ) } } @@ -133,7 +134,7 @@ impl X448PublicKey { encoding: &pyo3::PyAny, format: &pyo3::PyAny, ) -> CryptographyResult<&'p pyo3::types::PyBytes> { - utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, false) + utils::pkey_public_bytes(py, slf, &slf.borrow().pkey, encoding, format, false, true) } fn __richcmp__( diff --git a/src/rust/src/error.rs b/src/rust/src/error.rs index 689ae613e8bb..6699520cb397 100644 --- a/src/rust/src/error.rs +++ b/src/rust/src/error.rs @@ -74,13 +74,16 @@ impl From for pyo3::PyErr { .expect("Failed to append to list"); } exceptions::InternalError::new_err(( - "Unknown OpenSSL error. This error is commonly encountered + format!( + "Unknown OpenSSL error. This error is commonly encountered when another library is not cleaning up the OpenSSL error stack. If you are using cryptography with another library that uses OpenSSL try disabling it before reporting a bug. Otherwise please file an issue at https://github.com/pyca/cryptography/issues with - information on how to reproduce this.", + information on how to reproduce this. ({:?})", + errors + ), errors.to_object(py), )) }), diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py index b97d7634396e..00920868fc65 100644 --- a/tests/hazmat/primitives/test_dsa.py +++ b/tests/hazmat/primitives/test_dsa.py @@ -395,6 +395,8 @@ def test_public_key_equality(self, backend): assert key1 == key2 assert key1 != key3 assert key1 != object() + with pytest.raises(TypeError): + key1 < key2 # type: ignore[operator] @pytest.mark.supported( @@ -711,6 +713,10 @@ def test_private_bytes_encrypted_pem(self, backend, fmt, password): (serialization.Encoding.DER, serialization.PrivateFormat.Raw), (serialization.Encoding.Raw, serialization.PrivateFormat.Raw), (serialization.Encoding.X962, serialization.PrivateFormat.PKCS8), + ( + serialization.Encoding.SMIME, + serialization.PrivateFormat.TraditionalOpenSSL, + ), ], ) def test_private_bytes_rejects_invalid(self, encoding, fmt, backend): diff --git a/tests/hazmat/primitives/test_x25519.py b/tests/hazmat/primitives/test_x25519.py index ae4f382bc487..2b86d3d5e22b 100644 --- a/tests/hazmat/primitives/test_x25519.py +++ b/tests/hazmat/primitives/test_x25519.py @@ -254,6 +254,13 @@ def test_invalid_private_bytes(self, backend): serialization.NoEncryption(), ) + with pytest.raises(ValueError): + key.private_bytes( + serialization.Encoding.PEM, + serialization.PrivateFormat.TraditionalOpenSSL, + serialization.NoEncryption(), + ) + def test_invalid_public_bytes(self, backend): key = X25519PrivateKey.generate().public_key() with pytest.raises(ValueError): diff --git a/tests/x509/test_x509_crlbuilder.py b/tests/x509/test_x509_crlbuilder.py index 8633f8abba22..95c0677bb777 100644 --- a/tests/x509/test_x509_crlbuilder.py +++ b/tests/x509/test_x509_crlbuilder.py @@ -524,6 +524,10 @@ def test_sign_with_invalid_hash_ed448(self, backend): with pytest.raises(ValueError): builder.sign(private_key, hashes.SHA256(), backend) + @pytest.mark.supported( + only_if=lambda backend: backend.dsa_supported(), + skip_message="Requires OpenSSL with DSA support", + ) def test_sign_dsa_key(self, backend): private_key = DSA_KEY_2048.private_key(backend) invalidity_date = x509.InvalidityDate(