fix(proxy): probe scrypt instead of MD5 in the fips_mode OpenSSL check (#50305)

* fix(proxy): probe scrypt instead of MD5 in the fips_mode OpenSSL check

* test(proxy): fake a working scrypt so the fips_mode check tests pass on FIPS hosts
This commit is contained in:
yucheng-berri 2026-11-05 17:43:32 -08:00 • committed by GitHub
parent d96da7267b
commit 9f489430a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 19 additions and 18 deletions

View file

@ -23,7 +23,7 @@ def _fips_mode_setting(general_settings: Mapping[str, object]) -> bool | None:
def openssl_enforces_fips() -> bool:
"""Python's OpenSSL runs with FIPS-only defaults and itself refuses MD5."""
"""Python's OpenSSL runs with FIPS-only defaults and itself refuses scrypt."""
try:
import _hashlib
@ -32,7 +32,7 @@ def openssl_enforces_fips() -> bool:
except (ImportError, AttributeError, ValueError):
return False
try:
_hashlib.new("md5", b"", usedforsecurity=True)
_hashlib.scrypt(b"", salt=b"\0" * 16, n=2, r=1, p=1, dklen=16)
except ValueError:
return True
return False

View file

@ -1,7 +1,6 @@
import _hashlib
from collections.abc import Mapping
from contextlib import nullcontext
from typing import Final
from unittest.mock import Mock
import pytest
@ -17,30 +16,32 @@ from litellm.proxy.common_utils.fips import (
)
def _fake_openssl(monkeypatch: pytest.MonkeyPatch, *, fips_flag: int, md5_refused: bool) -> None:
real_new: Final = _hashlib.new
def new(name: str, data: bytes = b"", *, usedforsecurity: bool = True) -> _hashlib.HASH:
if name == "md5" and md5_refused:
raise _hashlib.UnsupportedDigestmodError("unsupported hash type")
return real_new(name, data, usedforsecurity=usedforsecurity)
def _fake_openssl(monkeypatch: pytest.MonkeyPatch, *, fips_flag: int, scrypt_refused: bool) -> None:
monkeypatch.setattr(_hashlib, "get_fips_mode", lambda: fips_flag)
monkeypatch.setattr(_hashlib, "new", new)
if scrypt_refused:
monkeypatch.setattr(_hashlib, "scrypt", Mock(side_effect=ValueError("[digital envelope routines] unsupported")))
else:
monkeypatch.setattr(_hashlib, "scrypt", Mock(return_value=b"\0" * 16))
@pytest.mark.parametrize(
"fips_flag, md5_refused, enforces",
"fips_flag, scrypt_refused, enforces",
[(1, True, True), (0, True, False), (1, False, False)],
ids=["fips", "fips-flag-off", "md5-allowed"],
ids=["fips", "fips-flag-off", "scrypt-allowed"],
)
def test_openssl_enforces_fips_needs_the_fips_flag_and_a_refused_md5(
monkeypatch: pytest.MonkeyPatch, fips_flag: int, md5_refused: bool, enforces: bool
def test_openssl_enforces_fips_needs_the_fips_flag_and_a_refused_scrypt(
monkeypatch: pytest.MonkeyPatch, fips_flag: int, scrypt_refused: bool, enforces: bool
) -> None:
_fake_openssl(monkeypatch, fips_flag=fips_flag, md5_refused=md5_refused)
_fake_openssl(monkeypatch, fips_flag=fips_flag, scrypt_refused=scrypt_refused)
assert openssl_enforces_fips() is enforces
def test_openssl_enforces_fips_while_md5_is_still_served_for_non_security_use(monkeypatch: pytest.MonkeyPatch) -> None:
_fake_openssl(monkeypatch, fips_flag=1, scrypt_refused=True)
monkeypatch.setattr(_hashlib, "new", Mock(return_value=Mock()))
assert openssl_enforces_fips() is True
def test_openssl_without_get_fips_mode_does_not_enforce_fips(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.delattr(_hashlib, "get_fips_mode")
assert openssl_enforces_fips() is False
@ -113,7 +114,7 @@ def test_enforce_fips_mode(
cryptography_fips: bool,
refusal: str | None,
) -> None:
_fake_openssl(monkeypatch, fips_flag=int(openssl_fips), md5_refused=openssl_fips)
_fake_openssl(monkeypatch, fips_flag=int(openssl_fips), scrypt_refused=openssl_fips)
monkeypatch.setattr(rust_openssl, "is_fips_enabled", lambda: cryptography_fips)
with pytest.raises(FipsModeError, match=refusal) if refusal else nullcontext():