RSA OAEP label support for OpenSSL 1.0.2+ (#3897)

* RSA OAEP label support for OpenSSL 1.0.2+

* changelog

* move around tests, address review feedback, use backend supported method

* unsupported padding catches this now
This commit is contained in:
Paul Kehrer 2017-09-09 07:03:50 +08:00 committed by Alex Gaynor
parent 52067bc300
commit d4bde9ce66
5 changed files with 153 additions and 18 deletions

View file

@ -38,6 +38,10 @@ Changelog
* Support :class:`~cryptography.hazmat.primitives.hashes.BLAKE2b` and
:class:`~cryptography.hazmat.primitives.hashes.BLAKE2s` with
:class:`~cryptography.hazmat.primitives.hmac.HMAC`.
* Added support for using labels with
:class:`~cryptography.hazmat.primitives.asymmetric.padding.OAEP` when using
OpenSSL 1.0.2 or greater.
.. _v2-0-3:

View file

@ -547,7 +547,11 @@ class Backend(object):
elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1):
return (
self._oaep_hash_supported(padding._mgf._algorithm) and
self._oaep_hash_supported(padding._algorithm)
self._oaep_hash_supported(padding._algorithm) and
(
(padding._label is None or len(padding._label) == 0) or
self._lib.Cryptography_HAS_RSA_OAEP_LABEL == 1
)
)
else:
return False

View file

@ -57,9 +57,6 @@ def _enc_dec_rsa(backend, key, data, padding):
_Reasons.UNSUPPORTED_PADDING
)
if padding._label is not None and padding._label != b"":
raise ValueError("This backend does not support OAEP labels.")
else:
raise UnsupportedAlgorithm(
"{0} is not supported by this backend.".format(
@ -106,6 +103,21 @@ def _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding):
res = backend._lib.EVP_PKEY_CTX_set_rsa_oaep_md(pkey_ctx, oaep_md)
backend.openssl_assert(res > 0)
if (
isinstance(padding, OAEP) and
padding._label is not None and
len(padding._label) > 0
):
# set0_rsa_oaep_label takes ownership of the char * so we need to
# copy it into some new memory
labelptr = backend._lib.OPENSSL_malloc(len(padding._label))
backend.openssl_assert(labelptr != backend._ffi.NULL)
backend._ffi.memmove(labelptr, padding._label, len(padding._label))
res = backend._lib.EVP_PKEY_CTX_set0_rsa_oaep_label(
pkey_ctx, labelptr, len(padding._label)
)
backend.openssl_assert(res == 1)
outlen = backend._ffi.new("size_t *", buf_size)
buf = backend._ffi.new("unsigned char[]", buf_size)
res = crypt(pkey_ctx, buf, outlen, data, len(data))

View file

@ -422,18 +422,6 @@ class TestOpenSSLRSA(object):
)
)
def test_unsupported_oaep_label_decrypt(self):
private_key = RSA_KEY_512.private_key(backend)
with pytest.raises(ValueError):
private_key.decrypt(
b"0" * 64,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
algorithm=hashes.SHA1(),
label=b"label"
)
)
class TestOpenSSLCMAC(object):
def test_unsupported_cipher(self):

View file

@ -38,8 +38,8 @@ from ...doubles import (
DummyAsymmetricPadding, DummyHashAlgorithm, DummyKeySerializationEncryption
)
from ...utils import (
load_pkcs1_vectors, load_rsa_nist_vectors, load_vectors_from_file,
raises_unsupported_algorithm
load_nist_vectors, load_pkcs1_vectors, load_rsa_nist_vectors,
load_vectors_from_file, raises_unsupported_algorithm
)
@ -218,6 +218,133 @@ class TestRSA(object):
assert public_num.n == public_num2.n
assert public_num.e == public_num2.e
@pytest.mark.parametrize(
"vector",
load_vectors_from_file(
os.path.join("asymmetric", "RSA", "oaep-label.txt"),
load_nist_vectors)
)
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=b"label"
)
),
skip_message="Does not support RSA OAEP labels"
)
def test_oaep_label_decrypt(self, vector, backend):
private_key = serialization.load_der_private_key(
binascii.unhexlify(vector["key"]), None, backend
)
assert vector["oaepdigest"] == b"SHA512"
decrypted = private_key.decrypt(
binascii.unhexlify(vector["input"]),
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA512()),
algorithm=hashes.SHA512(),
label=binascii.unhexlify(vector["oaeplabel"])
)
)
assert vector["output"][1:-1] == decrypted
@pytest.mark.parametrize(
("msg", "label"),
[
(b"amazing encrypted msg", b"some label"),
(b"amazing encrypted msg", b""),
]
)
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=b"label"
)
),
skip_message="Does not support RSA OAEP labels"
)
def test_oaep_label_roundtrip(self, msg, label, backend):
private_key = RSA_KEY_2048.private_key(backend)
ct = private_key.public_key().encrypt(
msg,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=label
)
)
pt = private_key.decrypt(
ct,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=label
)
)
assert pt == msg
@pytest.mark.parametrize(
("enclabel", "declabel"),
[
(b"label1", b"label2"),
(b"label3", b""),
(b"", b"label4"),
]
)
@pytest.mark.supported(
only_if=lambda backend: backend.rsa_padding_supported(
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=b"label"
)
),
skip_message="Does not support RSA OAEP labels"
)
def test_oaep_wrong_label(self, enclabel, declabel, backend):
private_key = RSA_KEY_2048.private_key(backend)
msg = b"test"
ct = private_key.public_key().encrypt(
msg, padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=enclabel
)
)
with pytest.raises(ValueError):
private_key.decrypt(
ct, padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=declabel
)
)
@pytest.mark.supported(
only_if=lambda backend: not backend.rsa_padding_supported(
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=b"label"
)
),
skip_message="Requires backend without RSA OAEP label support"
)
def test_unsupported_oaep_label_decrypt(self, backend):
private_key = RSA_KEY_512.private_key(backend)
with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_PADDING):
private_key.decrypt(
b"0" * 64,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
algorithm=hashes.SHA1(),
label=b"label"
)
)
def test_rsa_generate_invalid_backend():
pretend_backend = object()