From 1937220d0fab8f4b8ecb9a8f18c57d80b2bd3dab Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Nov 2025 14:27:28 +0000 Subject: [PATCH] Deprecate short TripleDES key lengths TripleDES now emits a deprecation warning when 8-byte (single DES) or 16-byte (two-key) keys are passed. In a future release, only 24-byte (192-bit) keys will be accepted. Users needing single DES or two-key Triple DES compatibility should expand the key themselves: - Single DES (8 bytes): key + key + key - Two-key 3DES (16 bytes): key + key[:8] --- CHANGELOG.rst | 5 ++++ docs/hazmat/decrepit/ciphers.rst | 9 +++++- .../hazmat/decrepit/ciphers/algorithms.py | 21 +++++++++++-- tests/hazmat/primitives/decrepit/test_3des.py | 30 ++++++++++++------- .../primitives/decrepit/test_algorithms.py | 16 ++++++++-- tests/hazmat/primitives/test_cmac.py | 4 +-- tests/hazmat/primitives/utils.py | 6 +++- 7 files changed, 72 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e4b674120644..f787f92c185a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -27,6 +27,11 @@ Changelog such keys are impossible to process in a constant-time manner. We do not believe keys with this problem are in wide use, however we may revert this change based on the feedback we receive. +* Deprecated passing 64-bit (8-byte) and 128-bit (16-byte) keys to + :class:`~cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES`. In a + future release, only 192-bit (24-byte) keys will be accepted. Users should + expand shorter keys themselves (e.g., for single DES: ``key + key + key``, + for two-key: ``key + key[:8]``). * Updated the minimum supported Rust version (MSRV) to 1.83.0, from 1.74.0. * Added support for loading elliptic curve keys that contain explicit encodings of the curves ``secp256r1``, ``secp384r1``, and ``secp521r1``. diff --git a/docs/hazmat/decrepit/ciphers.rst b/docs/hazmat/decrepit/ciphers.rst index 5a3b2a50f396..da1106b90ddb 100644 --- a/docs/hazmat/decrepit/ciphers.rst +++ b/docs/hazmat/decrepit/ciphers.rst @@ -54,9 +54,16 @@ object along with the appropriate :mod:`~cryptography.hazmat.primitives.ciphers. :param key: The secret key. This must be kept secret. Either ``64``, ``128``, or ``192`` :term:`bits` long. DES only uses ``56``, ``112``, or ``168`` bits of the key as there is a parity byte in each component - of the key. Some writing refers to there being up to three separate + of the key. Some writing refers to there being up to three separate keys that are each ``56`` bits long, they can simply be concatenated to produce the full key. + + .. deprecated:: 47.0.0 + + Passing 64-bit or 128-bit keys is deprecated. In a future release, + only 192-bit (24-byte) keys will be accepted. Users should expand + shorter keys themselves (e.g., for single DES: ``key + key + key``, + for two-key: ``key + key[:8]``). :type key: :term:`bytes-like` .. class:: CAST5(key) diff --git a/src/cryptography/hazmat/decrepit/ciphers/algorithms.py b/src/cryptography/hazmat/decrepit/ciphers/algorithms.py index 1a5479369897..703c8e4a3e47 100644 --- a/src/cryptography/hazmat/decrepit/ciphers/algorithms.py +++ b/src/cryptography/hazmat/decrepit/ciphers/algorithms.py @@ -4,6 +4,9 @@ from __future__ import annotations +import warnings + +from cryptography import utils from cryptography.hazmat.primitives._cipheralgorithm import ( BlockCipherAlgorithm, CipherAlgorithm, @@ -30,9 +33,23 @@ class TripleDES(BlockCipherAlgorithm): def __init__(self, key: bytes): if len(key) == 8: - key += key + key + warnings.warn( + "Single-key TripleDES (8-byte keys) is deprecated and " + "support will be removed in a future release. Use 24-byte " + "keys instead (e.g., key + key + key).", + utils.DeprecatedIn47, + stacklevel=2, + ) + key = key + key + key elif len(key) == 16: - key += key[:8] + warnings.warn( + "Two-key TripleDES (16-byte keys) is deprecated and " + "support will be removed in a future release. Use 24-byte " + "keys instead (e.g., key + key[:8]).", + utils.DeprecatedIn47, + stacklevel=2, + ) + key = key + key[:8] self.key = _verify_key_size(self, key) @property diff --git a/tests/hazmat/primitives/decrepit/test_3des.py b/tests/hazmat/primitives/decrepit/test_3des.py index e72c0a531641..62a2a1f7b136 100644 --- a/tests/hazmat/primitives/decrepit/test_3des.py +++ b/tests/hazmat/primitives/decrepit/test_3des.py @@ -21,7 +21,7 @@ @pytest.mark.supported( only_if=lambda backend: backend.cipher_supported( - algorithms.TripleDES(b"\x00" * 8), modes.CBC(b"\x00" * 8) + algorithms.TripleDES(b"\x00" * 24), modes.CBC(b"\x00" * 8) ), skip_message="Does not support TripleDES CBC", ) @@ -36,7 +36,9 @@ class TestTripleDESModeCBC: "TCBCvarkey.rsp", "TCBCvartext.rsp", ], - lambda keys, **kwargs: algorithms.TripleDES(binascii.unhexlify(keys)), + lambda keys, **kwargs: algorithms.TripleDES( + binascii.unhexlify(keys) * 3 + ), lambda iv, **kwargs: modes.CBC(binascii.unhexlify(iv)), ) @@ -53,7 +55,7 @@ class TestTripleDESModeCBC: @pytest.mark.supported( only_if=lambda backend: backend.cipher_supported( - algorithms.TripleDES(b"\x00" * 8), OFB(b"\x00" * 8) + algorithms.TripleDES(b"\x00" * 24), OFB(b"\x00" * 8) ), skip_message="Does not support TripleDES OFB", ) @@ -68,7 +70,9 @@ class TestTripleDESModeOFB: "TOFBvartext.rsp", "TOFBinvperm.rsp", ], - lambda keys, **kwargs: algorithms.TripleDES(binascii.unhexlify(keys)), + lambda keys, **kwargs: algorithms.TripleDES( + binascii.unhexlify(keys) * 3 + ), lambda iv, **kwargs: OFB(binascii.unhexlify(iv)), ) @@ -85,7 +89,7 @@ class TestTripleDESModeOFB: @pytest.mark.supported( only_if=lambda backend: backend.cipher_supported( - algorithms.TripleDES(b"\x00" * 8), CFB(b"\x00" * 8) + algorithms.TripleDES(b"\x00" * 24), CFB(b"\x00" * 8) ), skip_message="Does not support TripleDES CFB", ) @@ -100,7 +104,9 @@ class TestTripleDESModeCFB: "TCFB64varkey.rsp", "TCFB64vartext.rsp", ], - lambda keys, **kwargs: algorithms.TripleDES(binascii.unhexlify(keys)), + lambda keys, **kwargs: algorithms.TripleDES( + binascii.unhexlify(keys) * 3 + ), lambda iv, **kwargs: CFB(binascii.unhexlify(iv)), ) @@ -117,7 +123,7 @@ class TestTripleDESModeCFB: @pytest.mark.supported( only_if=lambda backend: backend.cipher_supported( - algorithms.TripleDES(b"\x00" * 8), CFB8(b"\x00" * 8) + algorithms.TripleDES(b"\x00" * 24), CFB8(b"\x00" * 8) ), skip_message="Does not support TripleDES CFB8", ) @@ -132,7 +138,9 @@ class TestTripleDESModeCFB8: "TCFB8varkey.rsp", "TCFB8vartext.rsp", ], - lambda keys, **kwargs: algorithms.TripleDES(binascii.unhexlify(keys)), + lambda keys, **kwargs: algorithms.TripleDES( + binascii.unhexlify(keys) * 3 + ), lambda iv, **kwargs: CFB8(binascii.unhexlify(iv)), ) @@ -149,7 +157,7 @@ class TestTripleDESModeCFB8: @pytest.mark.supported( only_if=lambda backend: backend.cipher_supported( - algorithms.TripleDES(b"\x00" * 8), modes.ECB() + algorithms.TripleDES(b"\x00" * 24), modes.ECB() ), skip_message="Does not support TripleDES ECB", ) @@ -164,7 +172,9 @@ class TestTripleDESModeECB: "TECBvarkey.rsp", "TECBvartext.rsp", ], - lambda keys, **kwargs: algorithms.TripleDES(binascii.unhexlify(keys)), + lambda keys, **kwargs: algorithms.TripleDES( + binascii.unhexlify(keys) * 3 + ), lambda **kwargs: modes.ECB(), ) diff --git a/tests/hazmat/primitives/decrepit/test_algorithms.py b/tests/hazmat/primitives/decrepit/test_algorithms.py index 27b3b6af5902..11cface601c1 100644 --- a/tests/hazmat/primitives/decrepit/test_algorithms.py +++ b/tests/hazmat/primitives/decrepit/test_algorithms.py @@ -8,6 +8,7 @@ import pytest +from cryptography import utils from cryptography.exceptions import _Reasons from cryptography.hazmat.decrepit.ciphers.algorithms import ( ARC4, @@ -72,9 +73,8 @@ def test_invalid_mode_algorithm(): class TestTripleDES: - @pytest.mark.parametrize("key", [b"0" * 16, b"0" * 32, b"0" * 48]) - def test_key_size(self, key): - cipher = TripleDES(binascii.unhexlify(key)) + def test_key_size(self): + cipher = TripleDES(binascii.unhexlify(b"0" * 48)) assert cipher.key_size == 192 def test_invalid_key_size(self): @@ -85,6 +85,16 @@ def test_invalid_key_type(self): with pytest.raises(TypeError, match="key must be bytes"): TripleDES("0" * 16) # type: ignore[arg-type] + def test_single_key_deprecated(self): + with pytest.warns(utils.DeprecatedIn47): + cipher = TripleDES(binascii.unhexlify(b"0" * 16)) + assert cipher.key_size == 192 + + def test_two_key_deprecated(self): + with pytest.warns(utils.DeprecatedIn47): + cipher = TripleDES(binascii.unhexlify(b"0" * 32)) + assert cipher.key_size == 192 + class TestBlowfish: @pytest.mark.parametrize( diff --git a/tests/hazmat/primitives/test_cmac.py b/tests/hazmat/primitives/test_cmac.py index 5e81563a6b14..00417a205395 100644 --- a/tests/hazmat/primitives/test_cmac.py +++ b/tests/hazmat/primitives/test_cmac.py @@ -81,7 +81,7 @@ def test_aes_verify(self, backend, params): @pytest.mark.supported( only_if=lambda backend: backend.cmac_algorithm_supported( - TripleDES(fake_key) + TripleDES(b"\x00" * 24) ), skip_message="Does not support CMAC.", ) @@ -102,7 +102,7 @@ def test_3des_generate(self, backend, params): @pytest.mark.supported( only_if=lambda backend: backend.cmac_algorithm_supported( - TripleDES(fake_key) + TripleDES(b"\x00" * 24) ), skip_message="Does not support CMAC.", ) diff --git a/tests/hazmat/primitives/utils.py b/tests/hazmat/primitives/utils.py index 0557694c96e9..ac97ce9d83b9 100644 --- a/tests/hazmat/primitives/utils.py +++ b/tests/hazmat/primitives/utils.py @@ -448,7 +448,11 @@ def _kbkdf_cmac_counter_mode_test(backend, prf, ctr_loc, brk_loc, params): break_location=brk_loc, ) - ko = ctrkdf.derive(binascii.unhexlify(params["ki"])) + ki = binascii.unhexlify(params["ki"]) + # TripleDES requires 24-byte keys. Expand 16-byte (2-key) to 24-byte. + if prf == "cmac_tdes2": + ki = ki + ki[:8] + ko = ctrkdf.derive(ki) assert binascii.hexlify(ko) == params["ko"]