Skip to content

trestle.core.canonicalization

trestle.core.canonicalization ¤

Canonical JSON helpers for reproducible OSCAL artifact digests.

Classes¤

Functions:¤

canonicalize_json_object(json_obj) ¤

Return RFC 8785 canonical UTF-8 bytes for a parsed JSON-compatible object.

Source code in trestle/core/canonicalization.py
54
55
56
57
58
59
def canonicalize_json_object(json_obj: Any) -> bytes:
    """Return RFC 8785 canonical UTF-8 bytes for a parsed JSON-compatible object."""
    try:
        return rfc8785.dumps(json_obj)
    except rfc8785.CanonicalizationError as error:
        raise TrestleError(f'Unable to canonicalize JSON object according to RFC 8785: {error}')

canonicalize_json_text(json_text) ¤

Parse JSON text and return its parsed object and canonical bytes.

Source code in trestle/core/canonicalization.py
42
43
44
45
46
47
48
49
50
51
def canonicalize_json_text(json_text: str) -> Tuple[Any, bytes]:
    """Parse JSON text and return its parsed object and canonical bytes."""
    try:
        json_obj = json.loads(
            json_text, object_pairs_hook=_object_pairs_without_duplicates, parse_constant=_reject_json_constant
        )
    except json.JSONDecodeError as error:
        raise TrestleError(f'Input is not valid JSON: {error}')

    return json_obj, canonicalize_json_object(json_obj)

digest_algorithm_name(algorithm) ¤

Return the hashlib and in-toto name for a supported OSCAL digest algorithm.

Source code in trestle/core/canonicalization.py
62
63
64
65
66
67
68
69
def digest_algorithm_name(algorithm: Algorithm) -> str:
    """Return the hashlib and in-toto name for a supported OSCAL digest algorithm."""
    # OSCAL's base model imports canonicalization, so defer this import to avoid a cycle.
    from trestle.oscal.common import Algorithm

    if not isinstance(algorithm, Algorithm):
        raise TrestleError(f'Unsupported digest algorithm: {algorithm}')
    return algorithm.value.lower().replace('sha-', 'sha').replace('-', '_')

digest_hex(data, algorithm) ¤

Return a hexadecimal digest using the selected OSCAL algorithm.

Source code in trestle/core/canonicalization.py
72
73
74
def digest_hex(data: bytes, algorithm: Algorithm) -> str:
    """Return a hexadecimal digest using the selected OSCAL algorithm."""
    return hashlib.new(digest_algorithm_name(algorithm), data).hexdigest()

load_canonical_json_file(path) ¤

Load a JSON document and return its parsed object and canonical bytes.

Source code in trestle/core/canonicalization.py
34
35
36
37
38
39
def load_canonical_json_file(path: pathlib.Path) -> Tuple[Any, bytes]:
    """Load a JSON document and return its parsed object and canonical bytes."""
    if not path.exists() or not path.is_file():
        raise TrestleError(f'JSON path does not exist or is not a file: {path}')

    return canonicalize_json_text(path.read_text(encoding=const.FILE_ENCODING))

parse_digest_algorithm(name) ¤

Resolve an in-toto digest name to a supported OSCAL algorithm.

Source code in trestle/core/canonicalization.py
77
78
79
80
81
82
83
84
def parse_digest_algorithm(name: Any) -> Algorithm:
    """Resolve an in-toto digest name to a supported OSCAL algorithm."""
    from trestle.oscal.common import Algorithm

    for algorithm in Algorithm:
        if digest_algorithm_name(algorithm) == name:
            return algorithm
    raise TrestleError(f'Unsupported digest algorithm: {name}')

sha256_digest_hex(data) ¤

Return a SHA-256 digest for compatibility with existing callers.

Source code in trestle/core/canonicalization.py
87
88
89
90
91
def sha256_digest_hex(data: bytes) -> str:
    """Return a SHA-256 digest for compatibility with existing callers."""
    from trestle.oscal.common import Algorithm

    return digest_hex(data, Algorithm.SHA_256)

handler: python