Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

md5 OpenSSL FIPS mode fix #7611 #7614

Merged
merged 4 commits into from May 5, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -85,6 +85,7 @@ Other contributors, listed alphabetically, are:
* Daniel Pizetta -- inheritance diagram improvements
* KINEBUCHI Tomohiko -- typing Sphinx as well as docutils
* Adrián Chaves (Gallaecio) -- coverage builder improvements
* Lars Hupfeldt Nielsen - OpenSSL FIPS mode md5 bug fix

Many thanks for all contributions!

Expand Down
1 change: 1 addition & 0 deletions CHANGES
Expand Up @@ -17,6 +17,7 @@ Bugs fixed
----------

* #7567: autodoc: parametrized types are shown twice for generic types
* #7611: md5 fails when OpenSSL FIPS is enabled

Testing
--------
Expand Down
5 changes: 2 additions & 3 deletions sphinx/builders/html/__init__.py
Expand Up @@ -13,7 +13,6 @@
import re
import sys
import warnings
from hashlib import md5
from os import path
from typing import Any, Dict, IO, Iterable, Iterator, List, Set, Tuple

Expand All @@ -38,7 +37,7 @@
from sphinx.locale import _, __
from sphinx.search import js_index
from sphinx.theming import HTMLThemeFactory
from sphinx.util import logging, progress_message, status_iterator
from sphinx.util import logging, progress_message, status_iterator, fips_safe_md5
from sphinx.util.docutils import is_html5_writer_available, new_document
from sphinx.util.fileutil import copy_asset
from sphinx.util.i18n import format_date
Expand Down Expand Up @@ -77,7 +76,7 @@ def get_stable_hash(obj: Any) -> str:
return get_stable_hash(list(obj.items()))
elif isinstance(obj, (list, tuple)):
obj = sorted(get_stable_hash(o) for o in obj)
return md5(str(obj).encode()).hexdigest()
return fips_safe_md5(str(obj).encode()).hexdigest()


class Stylesheet(str):
Expand Down
4 changes: 2 additions & 2 deletions sphinx/ext/inheritance_diagram.py
Expand Up @@ -38,7 +38,6 @@ class E(B): pass
import builtins
import inspect
import re
from hashlib import md5
from importlib import import_module
from typing import Any, Dict, Iterable, List, Tuple
from typing import cast
Expand All @@ -55,6 +54,7 @@ class E(B): pass
graphviz, figure_wrapper,
render_dot_html, render_dot_latex, render_dot_texinfo
)
from sphinx.util import fips_safe_md5
from sphinx.util.docutils import SphinxDirective
from sphinx.writers.html import HTMLTranslator
from sphinx.writers.latex import LaTeXTranslator
Expand Down Expand Up @@ -387,7 +387,7 @@ def run(self) -> List[Node]:

def get_graph_hash(node: inheritance_diagram) -> str:
encoded = (node['content'] + str(node['parts'])).encode()
return md5(encoded).hexdigest()[-10:]
return fips_safe_md5(encoded).hexdigest()[-10:]


def html_visit_inheritance_diagram(self: HTMLTranslator, node: inheritance_diagram) -> None:
Expand Down
18 changes: 17 additions & 1 deletion sphinx/util/__init__.py
Expand Up @@ -170,6 +170,22 @@ def __setstate__(self, state: Set[str]) -> None:
self._existing = state


def fips_safe_md5(data=b'', **kwargs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to rename this to md5() simply.

"""Wrapper around hashlib.md5

Attempt call with 'usedforsecurity=False' if we get a ValueError, which happens when
OpenSSL FIPS mode is enabled:
ValueError: error:060800A3:digital envelope routines:EVP_DigestInit_ex:disabled for fips

See: https://github.com/sphinx-doc/sphinx/issues/7611
"""

try:
return md5(data, **kwargs) # type: ignore
except ValueError:
return md5(data, **kwargs, usedforsecurity=False) # type: ignore


class DownloadFiles(dict):
"""A special dictionary for download files.

Expand All @@ -179,7 +195,7 @@ class DownloadFiles(dict):

def add_file(self, docname: str, filename: str) -> str:
if filename not in self:
digest = md5(filename.encode()).hexdigest()
digest = fips_safe_md5(filename.encode()).hexdigest()
dest = '%s/%s' % (digest, os.path.basename(filename))
self[filename] = (set(), dest)

Expand Down
7 changes: 3 additions & 4 deletions tests/test_build_html.py
Expand Up @@ -10,7 +10,6 @@

import os
import re
from hashlib import md5
from itertools import cycle, chain

import pytest
Expand All @@ -19,7 +18,7 @@
from sphinx.builders.html import validate_html_extra_path, validate_html_static_path
from sphinx.errors import ConfigError
from sphinx.testing.util import strip_escseq
from sphinx.util import docutils
from sphinx.util import docutils, fips_safe_md5
from sphinx.util.inventory import InventoryFile


Expand Down Expand Up @@ -450,9 +449,9 @@ def test_html_download(app):
@pytest.mark.sphinx('html', testroot='roles-download')
def test_html_download_role(app, status, warning):
app.build()
digest = md5(b'dummy.dat').hexdigest()
digest = fips_safe_md5(b'dummy.dat').hexdigest()
assert (app.outdir / '_downloads' / digest / 'dummy.dat').exists()
digest_another = md5(b'another/dummy.dat').hexdigest()
digest_another = fips_safe_md5(b'another/dummy.dat').hexdigest()
assert (app.outdir / '_downloads' / digest_another / 'dummy.dat').exists()

content = (app.outdir / 'index.html').read_text()
Expand Down