spdx_tools.spdx.validation.checksum_validator

 1# SPDX-FileCopyrightText: 2022 spdx contributors
 2#
 3# SPDX-License-Identifier: Apache-2.0
 4
 5import re
 6
 7from beartype.typing import Dict, List
 8
 9from spdx_tools.spdx.model import Checksum, ChecksumAlgorithm
10from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
11
12# in hexadecimal digits
13algorithm_length: Dict = {
14    ChecksumAlgorithm.SHA1: "40",
15    ChecksumAlgorithm.SHA224: "56",
16    ChecksumAlgorithm.SHA256: "64",
17    ChecksumAlgorithm.SHA384: "96",
18    ChecksumAlgorithm.SHA512: "128",
19    ChecksumAlgorithm.SHA3_256: "64",
20    ChecksumAlgorithm.SHA3_384: "96",
21    ChecksumAlgorithm.SHA3_512: "128",
22    ChecksumAlgorithm.BLAKE2B_256: "64",
23    ChecksumAlgorithm.BLAKE2B_384: "96",
24    ChecksumAlgorithm.BLAKE2B_512: "128",
25    ChecksumAlgorithm.BLAKE3: "256,",  # at least 256 bits
26    ChecksumAlgorithm.MD2: "32",
27    ChecksumAlgorithm.MD4: "32",
28    ChecksumAlgorithm.MD5: "32",
29    ChecksumAlgorithm.MD6: "0,512",  # between 0 and 512 bits
30    ChecksumAlgorithm.ADLER32: "8",
31}
32
33
34def validate_checksums(checksums: List[Checksum], parent_id: str, spdx_version: str) -> List[ValidationMessage]:
35    validation_messages = []
36    for checksum in checksums:
37        validation_messages.extend(validate_checksum(checksum, parent_id, spdx_version))
38
39    return validation_messages
40
41
42def validate_checksum(checksum: Checksum, parent_id: str, spdx_version: str) -> List[ValidationMessage]:
43    validation_messages = []
44    algorithm = checksum.algorithm
45    context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.CHECKSUM, full_element=checksum)
46
47    if spdx_version == "SPDX-2.2" and algorithm in [
48        ChecksumAlgorithm.SHA3_512,
49        ChecksumAlgorithm.SHA3_384,
50        ChecksumAlgorithm.SHA3_256,
51        ChecksumAlgorithm.BLAKE3,
52        ChecksumAlgorithm.BLAKE2B_512,
53        ChecksumAlgorithm.BLAKE2B_384,
54        ChecksumAlgorithm.BLAKE2B_256,
55        ChecksumAlgorithm.ADLER32,
56    ]:
57        return [ValidationMessage(f"{checksum.algorithm.name} is not supported in SPDX-2.2", context)]
58
59    if not re.match("^[0-9a-f]{" + algorithm_length[algorithm] + "}$", checksum.value):
60        if algorithm == ChecksumAlgorithm.BLAKE3:
61            length = "at least 256"
62        elif algorithm == ChecksumAlgorithm.MD6:
63            length = "between 0 and 512"
64        else:
65            length = algorithm_length[algorithm]
66        validation_messages.append(
67            ValidationMessage(
68                f"value of {algorithm} must consist of {length} lowercase hexadecimal digits, but is: "
69                f"{checksum.value} (length: {len(checksum.value)} digits)",
70                context,
71            )
72        )
73
74    return validation_messages
algorithm_length: dict = {<ChecksumAlgorithm.SHA1: 1>: '40', <ChecksumAlgorithm.SHA224: 2>: '56', <ChecksumAlgorithm.SHA256: 3>: '64', <ChecksumAlgorithm.SHA384: 4>: '96', <ChecksumAlgorithm.SHA512: 5>: '128', <ChecksumAlgorithm.SHA3_256: 6>: '64', <ChecksumAlgorithm.SHA3_384: 7>: '96', <ChecksumAlgorithm.SHA3_512: 8>: '128', <ChecksumAlgorithm.BLAKE2B_256: 9>: '64', <ChecksumAlgorithm.BLAKE2B_384: 10>: '96', <ChecksumAlgorithm.BLAKE2B_512: 11>: '128', <ChecksumAlgorithm.BLAKE3: 12>: '256,', <ChecksumAlgorithm.MD2: 13>: '32', <ChecksumAlgorithm.MD4: 14>: '32', <ChecksumAlgorithm.MD5: 15>: '32', <ChecksumAlgorithm.MD6: 16>: '0,512', <ChecksumAlgorithm.ADLER32: 17>: '8'}
def validate_checksums( checksums: list[spdx_tools.spdx.model.checksum.Checksum], parent_id: str, spdx_version: str) -> list[spdx_tools.spdx.validation.validation_message.ValidationMessage]:
35def validate_checksums(checksums: List[Checksum], parent_id: str, spdx_version: str) -> List[ValidationMessage]:
36    validation_messages = []
37    for checksum in checksums:
38        validation_messages.extend(validate_checksum(checksum, parent_id, spdx_version))
39
40    return validation_messages
def validate_checksum( checksum: spdx_tools.spdx.model.checksum.Checksum, parent_id: str, spdx_version: str) -> list[spdx_tools.spdx.validation.validation_message.ValidationMessage]:
43def validate_checksum(checksum: Checksum, parent_id: str, spdx_version: str) -> List[ValidationMessage]:
44    validation_messages = []
45    algorithm = checksum.algorithm
46    context = ValidationContext(parent_id=parent_id, element_type=SpdxElementType.CHECKSUM, full_element=checksum)
47
48    if spdx_version == "SPDX-2.2" and algorithm in [
49        ChecksumAlgorithm.SHA3_512,
50        ChecksumAlgorithm.SHA3_384,
51        ChecksumAlgorithm.SHA3_256,
52        ChecksumAlgorithm.BLAKE3,
53        ChecksumAlgorithm.BLAKE2B_512,
54        ChecksumAlgorithm.BLAKE2B_384,
55        ChecksumAlgorithm.BLAKE2B_256,
56        ChecksumAlgorithm.ADLER32,
57    ]:
58        return [ValidationMessage(f"{checksum.algorithm.name} is not supported in SPDX-2.2", context)]
59
60    if not re.match("^[0-9a-f]{" + algorithm_length[algorithm] + "}$", checksum.value):
61        if algorithm == ChecksumAlgorithm.BLAKE3:
62            length = "at least 256"
63        elif algorithm == ChecksumAlgorithm.MD6:
64            length = "between 0 and 512"
65        else:
66            length = algorithm_length[algorithm]
67        validation_messages.append(
68            ValidationMessage(
69                f"value of {algorithm} must consist of {length} lowercase hexadecimal digits, but is: "
70                f"{checksum.value} (length: {len(checksum.value)} digits)",
71                context,
72            )
73        )
74
75    return validation_messages