diff --git a/libervia/backend/plugins/plugin_xep_0373/__init__.py b/libervia/backend/plugins/plugin_xep_0373/__init__.py index 06f1a0eb..ba220aeb 100644 --- a/libervia/backend/plugins/plugin_xep_0373/__init__.py +++ b/libervia/backend/plugins/plugin_xep_0373/__init__.py @@ -602,8 +602,11 @@ class XEP_0373: if include_rpad: # XEP-0373 doesn't specify bounds for the length of the random padding. This - # uses the bounds specified in XEP-0420 for the closely related rpad affix. - rpad_length = secrets.randbelow(201) + # uses the bounds specified in XEP-0420 for the closely related rpad affix. We + # use a minimum of 50 characters to ensure meaningful protection against + # length-based side channel attacks. XEP-0420 specifies 0-200 characters, but + # 0-length padding defeats the security purpose of rpad. + rpad_length = secrets.randbelow(151) + 50 # Range [50, 200] rpad_content = "".join( secrets.choice(string.digits + string.ascii_letters + string.punctuation) for __ in range(rpad_length) diff --git a/tests/unit/test_plugin_xep_0373.py b/tests/unit/test_plugin_xep_0373.py index 6cb291b8..d0f4dc9b 100644 --- a/tests/unit/test_plugin_xep_0373.py +++ b/tests/unit/test_plugin_xep_0373.py @@ -32,7 +32,8 @@ def test_signcrypt_element() -> None: signcrypt_elt.children.remove(rpad_elt) signcrypt_elt.children.remove(time_elt) - assert rpad + # Our implementation uses a minimum of 50 characters for security + assert len(rpad) >= 50 assert (datetime.now(timezone.utc) - timestamp) < timedelta(seconds=10) assert signcrypt_elt.toXml() == ( "" @@ -63,7 +64,8 @@ def test_sign_element_with_rpad() -> None: sign_elt.children.remove(rpad_elt) sign_elt.children.remove(time_elt) - assert rpad + # Our implementation uses a minimum of 50 characters for security + assert len(rpad) >= 50 assert (datetime.now(timezone.utc) - timestamp) < timedelta(seconds=10) assert sign_elt.toXml() == ( "" @@ -113,7 +115,8 @@ def test_crypt_element_with_recipients() -> None: crypt_elt.children.remove(rpad_elt) crypt_elt.children.remove(time_elt) - assert rpad + # Our implementation uses a minimum of 50 characters for security + assert len(rpad) >= 50 assert (datetime.now(timezone.utc) - timestamp) < timedelta(seconds=10) assert crypt_elt.toXml() == ( "" @@ -139,7 +142,8 @@ def test_crypt_element_without_recipients() -> None: crypt_elt.children.remove(rpad_elt) crypt_elt.children.remove(time_elt) - assert rpad + # Our implementation uses a minimum of 50 characters for security + assert len(rpad) >= 50 assert (datetime.now(timezone.utc) - timestamp) < timedelta(seconds=10) assert crypt_elt.toXml() == ( ""