diff --git a/CHANGELOG.md b/CHANGELOG.md index c3722703..525b95e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,25 @@ +## v7.6.0 (2024-08-14) + +### Feature + +* feat: `HashType.from_composite_str` for Blake2b, SHA3, Blake3 (#663) + +The code mistreated hashes for Blake2b and SHA3. +Code for explicitly handling SHA1 & BLAKE3 was added, as those have no +variants defined in the CycloneDX specification. + +fixes #652 + +--------- + +Signed-off-by: Michael Schlenker <michael.schlenker@contact-software.com> +Co-authored-by: Michael Schlenker <michael.schlenker@contact-software.com> +Co-authored-by: Jan Kowalleck <jan.kowalleck@gmail.com> ([`c59036e`](https://github.com/CycloneDX/cyclonedx-python-lib/commit/c59036e06ddc97284f82efbbc168dc2d89d090d1)) + + ## v7.5.1 (2024-07-08) ### Fix diff --git a/cyclonedx/__init__.py b/cyclonedx/__init__.py index 26e82d7b..94522e30 100644 --- a/cyclonedx/__init__.py +++ b/cyclonedx/__init__.py @@ -20,4 +20,4 @@ # !! version is managed by semantic_release # do not use typing here, or else `semantic_release` might have issues finding the variable -__version__ = "7.5.1" # noqa:Q000 +__version__ = "7.6.0" # noqa:Q000 diff --git a/cyclonedx/model/__init__.py b/cyclonedx/model/__init__.py index f45f99bb..c074a701 100644 --- a/cyclonedx/model/__init__.py +++ b/cyclonedx/model/__init__.py @@ -418,6 +418,11 @@ def from_composite_str(composite_hash: str) -> 'HashType': Composite Hash string of the format `HASH_ALGORITHM`:`HASH_VALUE`. Example: `sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b`. + Valid case insensitive prefixes are: + `md5`, `sha1`, `sha256`, `sha384`, `sha512`, `blake2b256`, `blake2b384`, `blake2b512`, + `blake2256`, `blake2384`, `blake2512`, `sha3-256`, `sha3-384`, `sha3-512`, + `blake3`. + Raises: `UnknownHashTypeException` if the type of hash cannot be determined. @@ -432,17 +437,37 @@ def from_composite_str(composite_hash: str) -> 'HashType': alg=HashAlgorithm.MD5, content=parts[1].lower() ) + elif algorithm_prefix[0:4] == 'sha3': + return HashType( + alg=getattr(HashAlgorithm, f'SHA3_{algorithm_prefix[5:]}'), + content=parts[1].lower() + ) + elif algorithm_prefix == 'sha1': + return HashType( + alg=HashAlgorithm.SHA_1, + content=parts[1].lower() + ) elif algorithm_prefix[0:3] == 'sha': + # This is actually SHA2... return HashType( alg=getattr(HashAlgorithm, f'SHA_{algorithm_prefix[3:]}'), content=parts[1].lower() ) + elif algorithm_prefix[0:7] == 'blake2b': + return HashType( + alg=getattr(HashAlgorithm, f'BLAKE2B_{algorithm_prefix[7:]}'), + content=parts[1].lower() + ) elif algorithm_prefix[0:6] == 'blake2': return HashType( - alg=getattr(HashAlgorithm, f'BLAKE2b_{algorithm_prefix[6:]}'), + alg=getattr(HashAlgorithm, f'BLAKE2B_{algorithm_prefix[6:]}'), + content=parts[1].lower() + ) + elif algorithm_prefix[0:6] == 'blake3': + return HashType( + alg=HashAlgorithm.BLAKE3, content=parts[1].lower() ) - raise UnknownHashTypeException(f'Unable to determine hash type from {composite_hash!r}') def __init__( diff --git a/docs/conf.py b/docs/conf.py index ebe26020..ffa0cd72 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ # The full version, including alpha/beta/rc tags # !! version is managed by semantic_release -release = '7.5.1' +release = '7.6.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 95095fb1..87d2a456 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "cyclonedx-python-lib" # !! version is managed by semantic_release -version = "7.5.1" +version = "7.6.0" description = "Python library for CycloneDX" authors = [ "Paul Horton ", @@ -82,8 +82,8 @@ xml-validation = ["lxml"] [tool.poetry.group.dev.dependencies] ddt = "1.7.2" -coverage = "7.6.0" -flake8 = { version="7.1.0", python=">=3.8.1" } +coverage = "7.6.1" +flake8 = { version="7.1.1", python=">=3.8.1" } flake8-annotations = { version="3.1.1", python=">=3.8.1" } flake8-bugbear = { version="24.4.26", python=">=3.8.1" } flake8-isort = "6.1.1" @@ -92,8 +92,8 @@ flake8-use-fstring = "1.4" pep8-naming = "0.14.1" isort = "5.13.2" autopep8 = "2.3.1" -mypy = "1.11.0" -tox = "4.16.0" +mypy = "1.11.1" +tox = "4.18.0" xmldiff = "2.7.0" bandit = "1.7.9" diff --git a/tests/test_model.py b/tests/test_model.py index 770a4d6c..5c6bb9ac 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -261,6 +261,23 @@ def test_hash_type_from_hashlib_alg_throws_on_unknown(self) -> None: HashAlgorithm.SHA_256, '806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b'), ('MD5', 'MD5:dc26cd71b80d6757139f38156a43c545', HashAlgorithm.MD5, 'dc26cd71b80d6757139f38156a43c545'), + ('sha3-256', 'sha3-256:f43909a5e6420ee26b710718f296c7be85ba393e6b218107811067f49ea80101', + HashAlgorithm.SHA3_256, 'f43909a5e6420ee26b710718f296c7be85ba393e6b218107811067f49ea80101'), + ('sha1', 'sha1:b82b9f695a3ae28053cb3776d2132ab625798055', + HashAlgorithm.SHA_1, 'b82b9f695a3ae28053cb3776d2132ab625798055'), + # Name format as used by 'openssl dgst and the Blake2 RFC' + ('blake2b512', + 'blake2b512:6d518ac5c7a022e954ecb21b8bf68d7f5c52e3c3579cd96f3bde4' + 'f76daaaa69a96a5eee268fb8fa2745930c37f0672424136b538878474bc4f586a63e13ae23f', + HashAlgorithm.BLAKE2B_512, + '6d518ac5c7a022e954ecb21b8bf68d7f5c52e3c3579cd96f3bde4f76daaaa69a' + '96a5eee268fb8fa2745930c37f0672424136b538878474bc4f586a63e13ae23f'), + ('blake2512', + 'blake2512:6d518ac5c7a022e954ecb21b8bf68d7f5c52e3c3579cd96f3bde4' + 'f76daaaa69a96a5eee268fb8fa2745930c37f0672424136b538878474bc4f586a63e13ae23f', + HashAlgorithm.BLAKE2B_512, + '6d518ac5c7a022e954ecb21b8bf68d7f5c52e3c3579cd96f3bde4f76daaaa69a' + '96a5eee268fb8fa2745930c37f0672424136b538878474bc4f586a63e13ae23f'), ) def test_hash_type_from_composite_str(self, composite: str, e_alg: HashAlgorithm, e_content: str) -> None: h = HashType.from_composite_str(composite)