Skip to content

Commit 39ef00a

Browse files
committed
Allow RSA signing with raw data (without a DigestInfo)
1 parent aca6952 commit 39ef00a

File tree

4 files changed

+119
-5
lines changed

4 files changed

+119
-5
lines changed

src/cryptography/hazmat/primitives/asymmetric/rsa.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def sign(
4040
self,
4141
data: bytes,
4242
padding: AsymmetricPadding,
43-
algorithm: asym_utils.Prehashed | hashes.HashAlgorithm,
43+
algorithm: None | asym_utils.Prehashed | hashes.HashAlgorithm,
4444
) -> bytes:
4545
"""
4646
Signs the data.

src/rust/src/backend/rsa.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,16 @@ impl RsaPrivateKey {
290290
padding: &pyo3::Bound<'p, pyo3::PyAny>,
291291
algorithm: &pyo3::Bound<'p, pyo3::PyAny>,
292292
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyAny>> {
293-
let (data, algorithm) =
294-
utils::calculate_digest_and_algorithm(py, data.as_bytes(), algorithm)?;
293+
let (data, algorithm) = {
294+
if algorithm.is_none() {
295+
(
296+
utils::BytesOrPyBytes::Bytes(data.as_bytes()),
297+
algorithm.clone(),
298+
)
299+
} else {
300+
utils::calculate_digest_and_algorithm(py, data.as_bytes(), algorithm)?
301+
}
302+
};
295303

296304
let mut ctx = openssl::pkey_ctx::PkeyCtx::new(&self.pkey)?;
297305
ctx.sign_init().map_err(|_| {
@@ -441,8 +449,16 @@ impl RsaPublicKey {
441449
padding: &pyo3::Bound<'_, pyo3::PyAny>,
442450
algorithm: &pyo3::Bound<'_, pyo3::PyAny>,
443451
) -> CryptographyResult<()> {
444-
let (data, algorithm) =
445-
utils::calculate_digest_and_algorithm(py, data.as_bytes(), algorithm)?;
452+
let (data, algorithm) = {
453+
if algorithm.is_none() {
454+
(
455+
utils::BytesOrPyBytes::Bytes(data.as_bytes()),
456+
algorithm.clone(),
457+
)
458+
} else {
459+
utils::calculate_digest_and_algorithm(py, data.as_bytes(), algorithm)?
460+
}
461+
};
446462

447463
let mut ctx = openssl::pkey_ctx::PkeyCtx::new(&self.pkey)?;
448464
ctx.verify_init()?;

tests/hazmat/primitives/test_rsa.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@
5151
)
5252
from .utils import (
5353
_check_rsa_private_numbers,
54+
compute_rsa_hash_digest,
5455
generate_rsa_verification_test,
56+
generate_rsa_verification_without_digest_test,
5557
skip_fips_traditional_openssl,
5658
)
5759

@@ -442,6 +444,49 @@ def test_pkcs1v15_signing(self, backend, subtests):
442444
)
443445
assert binascii.hexlify(signature) == example["signature"]
444446

447+
@pytest.mark.supported(
448+
only_if=lambda backend: backend.rsa_padding_supported(
449+
padding.PKCS1v15()
450+
),
451+
skip_message="Does not support PKCS1v1.5.",
452+
)
453+
@pytest.mark.supported(
454+
only_if=lambda backend: backend.signature_hash_supported(
455+
hashes.SHA1()
456+
),
457+
skip_message="Does not support SHA1 signature.",
458+
)
459+
def test_pkcs1v15_signing_without_digest(self, backend, subtests):
460+
vectors = _flatten_pkcs1_examples(
461+
load_vectors_from_file(
462+
os.path.join("asymmetric", "RSA", "pkcs1v15sign-vectors.txt"),
463+
load_pkcs1_vectors,
464+
)
465+
)
466+
for private, public, example in vectors:
467+
with subtests.test():
468+
private_key = rsa.RSAPrivateNumbers(
469+
p=private["p"],
470+
q=private["q"],
471+
d=private["private_exponent"],
472+
dmp1=private["dmp1"],
473+
dmq1=private["dmq1"],
474+
iqmp=private["iqmp"],
475+
public_numbers=rsa.RSAPublicNumbers(
476+
e=private["public_exponent"], n=private["modulus"]
477+
),
478+
).private_key(backend, unsafe_skip_rsa_key_validation=True)
479+
signature = private_key.sign(
480+
binascii.unhexlify(
481+
compute_rsa_hash_digest(
482+
backend, hashes.SHA1(), example["message"]
483+
)
484+
),
485+
padding.PKCS1v15(),
486+
None,
487+
)
488+
assert binascii.hexlify(signature) == example["signature"]
489+
445490
@pytest.mark.supported(
446491
only_if=lambda backend: backend.rsa_padding_supported(
447492
padding.PSS(
@@ -1522,6 +1567,26 @@ class TestRSAPKCS1Verification:
15221567
)
15231568
)
15241569

1570+
test_rsa_pkcs1v15_verify_sha1_without_digest = pytest.mark.supported(
1571+
only_if=lambda backend: (
1572+
backend.signature_hash_supported(hashes.SHA1())
1573+
and backend.rsa_padding_supported(padding.PKCS1v15())
1574+
),
1575+
skip_message="Does not support SHA1 and PKCS1v1.5.",
1576+
)(
1577+
generate_rsa_verification_without_digest_test(
1578+
load_rsa_nist_vectors,
1579+
os.path.join("asymmetric", "RSA", "FIPS_186-2"),
1580+
[
1581+
"SigGen15_186-2.rsp",
1582+
"SigGen15_186-3.rsp",
1583+
"SigVer15_186-3.rsp",
1584+
],
1585+
hashes.SHA1(),
1586+
lambda params, hash_alg: padding.PKCS1v15(),
1587+
)
1588+
)
1589+
15251590
test_rsa_pkcs1v15_verify_sha224 = pytest.mark.supported(
15261591
only_if=lambda backend: (
15271592
backend.signature_hash_supported(hashes.SHA224())

tests/hazmat/primitives/utils.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@
3737

3838
from ...utils import load_vectors_from_file
3939

40+
_hash_alg_oids = {
41+
"sha1": binascii.unhexlify(b"3021300906052b0e03021a05000414"),
42+
"sha224": binascii.unhexlify(b"302d300d06096086480165030402040500041c"),
43+
"sha256": binascii.unhexlify(b"3031300d060960864801650304020105000420"),
44+
"sha384": binascii.unhexlify(b"3041300d060960864801650304020205000430"),
45+
"sha512": binascii.unhexlify(b"3051300d060960864801650304020305000440"),
46+
}
47+
4048

4149
def _load_all_params(path, file_names, param_loader):
4250
all_params = []
@@ -47,6 +55,13 @@ def _load_all_params(path, file_names, param_loader):
4755
return all_params
4856

4957

58+
def compute_rsa_hash_digest(backend, hash_alg, msg):
59+
oid = _hash_alg_oids[hash_alg.name]
60+
h = hashes.Hash(hash_alg, backend=backend)
61+
h.update(binascii.unhexlify(msg))
62+
return binascii.hexlify(oid) + binascii.hexlify(h.finalize())
63+
64+
5065
def generate_encrypt_test(
5166
param_loader, path, file_names, cipher_factory, mode_factory
5267
):
@@ -497,6 +512,24 @@ def test_rsa_verification(self, backend, subtests):
497512
return test_rsa_verification
498513

499514

515+
def generate_rsa_verification_without_digest_test(
516+
param_loader, path, file_names, hash_alg, pad_factory
517+
):
518+
def test_rsa_verification(self, backend, subtests):
519+
all_params = _load_all_params(path, file_names, param_loader)
520+
all_params = [
521+
i for i in all_params if i["algorithm"] == hash_alg.name.upper()
522+
]
523+
for params in all_params:
524+
with subtests.test():
525+
params["msg"] = compute_rsa_hash_digest(
526+
backend, hash_alg, params["msg"]
527+
)
528+
rsa_verification_test(backend, params, None, pad_factory)
529+
530+
return test_rsa_verification
531+
532+
500533
def rsa_verification_test(backend, params, hash_alg, pad_factory):
501534
public_numbers = rsa.RSAPublicNumbers(
502535
e=params["public_exponent"], n=params["modulus"]

0 commit comments

Comments
 (0)