Skip to content

ISCC - ISO Reference#

The following functions are the reference implementations of ISO 24138:

ISO 24138 / 5.1 Meta-Code#

gen_meta_code_v0(name, description=None, meta=None, bits=ic.core_opts.meta_bits) #

Create an ISCC Meta-Code with the algorithm version 0.

Parameters:

Name Type Description Default
name str

Name or title of the work manifested by the digital asset

required
description Optional[str]

Optional description for disambiguation

None
meta Optional[Union[dict,str]

Dict or Data-URL string with extended metadata

None
bits int

Bit-length of resulting Meta-Code (multiple of 64)

ic.core_opts.meta_bits

Returns:

Type Description
dict

ISCC object with possible fields: iscc, name, description, metadata, metahash

Source code in iscc_core\code_meta.py
def gen_meta_code_v0(name, description=None, meta=None, bits=ic.core_opts.meta_bits):
    # type: (str, Optional[str], Optional[ic.Meta], int) -> dict
    """
    Create an ISCC Meta-Code with the algorithm version 0.

    :param str name: Name or title of the work manifested by the digital asset
    :param Optional[str] description: Optional description for disambiguation
    :param Optional[Union[dict,str] meta: Dict or Data-URL string with extended metadata
    :param int bits: Bit-length of resulting Meta-Code (multiple of 64)
    :return: ISCC object with possible fields: iscc, name, description, metadata, metahash
    :rtype: dict
    """

    # 1. Normalize `name`
    name = "" if name is None else name
    name = text_clean(name)
    name = text_remove_newlines(name)
    name = text_trim(name, ic.core_opts.meta_trim_name)

    if not name:
        raise ValueError("Meta-Code requires non-empty name element (after normalization)")

    # 2. Normalize `description`
    description = "" if description is None else description
    description = text_clean(description)
    description = text_trim(description, ic.core_opts.meta_trim_description)

    # Calculate meta_code, metahash, and output metadata values for the different input cases
    if meta:
        if isinstance(meta, str):
            # Data-URL expected
            durl = meta
            payload = DataURL.from_url(durl).data
            meta_code_digest = soft_hash_meta_v0(name, payload)
            metahash = ic.multi_hash_blake3(payload)
            metadata_value = durl
        elif isinstance(meta, dict):
            payload = jcs.canonicalize(meta)
            meta_code_digest = soft_hash_meta_v0(name, payload)
            metahash = ic.multi_hash_blake3(payload)
            media_type = "application/ld+json" if "@context" in meta else "application/json"
            durl_obj = DataURL.from_data(media_type, base64_encode=True, data=payload)
            metadata_value = durl_obj.url
        else:
            raise TypeError(f"metadata must be Data-URL string or dict not {type(meta)}")
    else:
        payload = " ".join((name, description)).strip().encode("utf-8")
        meta_code_digest = soft_hash_meta_v0(name, description)
        metahash = ic.multi_hash_blake3(payload)
        metadata_value = None

    meta_code = ic.encode_component(
        mtype=ic.MT.META,
        stype=ic.ST.NONE,
        version=ic.VS.V0,
        bit_length=bits,
        digest=meta_code_digest,
    )
    iscc = "ISCC:" + meta_code

    # Build result
    result = {"iscc": iscc}
    if name:
        result["name"] = name
    if description:
        result["description"] = description
    if metadata_value:
        result["meta"] = metadata_value

    result["metahash"] = metahash

    return result

ISO 24138 / 5.3 Text-Code#

gen_text_code_v0(text, bits=ic.core_opts.text_bits) #

Create an ISCC Text-Code with algorithm v0.

Note

Any markup (like HTML tags or markdown) should be removed from the plain-text before passing it to this function.

Parameters:

Name Type Description Default
text str

Text for Text-Code creation

required
bits int

Bit-length of ISCC Code Hash (default 64)

ic.core_opts.text_bits

Returns:

Type Description
dict

ISCC schema instance with Text-Code and an aditional property characters

Source code in iscc_core\code_content_text.py
def gen_text_code_v0(text, bits=ic.core_opts.text_bits):
    # type: (str, int) -> dict
    """
    Create an ISCC Text-Code with algorithm v0.

    !!! note
        Any markup (like HTML tags or markdown) should be removed from the plain-text
        before passing it to this function.

    :param str text: Text for Text-Code creation
    :param int bits: Bit-length of ISCC Code Hash (default 64)
    :return: ISCC schema instance with Text-Code and an aditional property `characters`
    :rtype: dict
    """

    text = text_collapse(text)
    characters = len(text)
    digest = soft_hash_text_v0(text)

    text_code = ic.encode_component(
        mtype=ic.MT.CONTENT,
        stype=ic.ST_CC.TEXT,
        version=ic.VS.V0,
        bit_length=bits,
        digest=digest,
    )

    iscc = "ISCC:" + text_code

    return dict(iscc=iscc, characters=characters)

ISO 24138 / 5.4 Image-Code#

gen_image_code_v0(pixels, bits=ic.core_opts.image_bits) #

Create an ISCC Content-Code Image with algorithm v0.

Parameters:

Name Type Description Default
pixels Sequence[int]

Normalized image pixels (32x32 flattened gray values)

required
bits int

Bit-length of ISCC Content-Code Image (default 64).

ic.core_opts.image_bits

Returns:

Type Description
ISCC

ISCC object with Content-Code Image.

Source code in iscc_core\code_content_image.py
def gen_image_code_v0(pixels, bits=ic.core_opts.image_bits):
    # type: (Sequence[int], int) -> dict
    """
    Create an ISCC Content-Code Image with algorithm v0.

    :param Sequence[int] pixels: Normalized image pixels (32x32 flattened gray values)
    :param int bits: Bit-length of ISCC Content-Code Image (default 64).
    :return: ISCC object with Content-Code Image.
    :rtype: ISCC
    """
    digest = soft_hash_image_v0(pixels, bits=bits)
    image_code = ic.encode_component(
        mtype=ic.MT.CONTENT,
        stype=ic.ST_CC.IMAGE,
        version=ic.VS.V0,
        bit_length=bits,
        digest=digest,
    )
    iscc = "ISCC:" + image_code
    return {"iscc": iscc}

ISO 24138 / 5.5 Audio-Code#

gen_audio_code_v0(cv, bits=ic.core_opts.audio_bits) #

Create an ISCC Content-Code Audio with algorithm v0.

Parameters:

Name Type Description Default
cv Iterable[int]

Chromaprint vector

required
bits int

Bit-length resulting Content-Code Audio (multiple of 64)

ic.core_opts.audio_bits

Returns:

Type Description
dict

ISCC object with Content-Code Audio

Source code in iscc_core\code_content_audio.py
def gen_audio_code_v0(cv, bits=ic.core_opts.audio_bits):
    # type: (Iterable[int], int) -> dict
    """
    Create an ISCC Content-Code Audio with algorithm v0.

    :param Iterable[int] cv: Chromaprint vector
    :param int bits: Bit-length resulting Content-Code Audio (multiple of 64)
    :return: ISCC object with Content-Code Audio
    :rtype: dict
    """
    digest = soft_hash_audio_v0(cv, bits=bits)
    audio_code = ic.encode_component(
        mtype=ic.MT.CONTENT,
        stype=ic.ST_CC.AUDIO,
        version=ic.VS.V0,
        bit_length=bits,
        digest=digest,
    )
    iscc = "ISCC:" + audio_code
    return {"iscc": iscc}

ISO 24138 / 5.6 Video-Code#

gen_video_code_v0(frame_sigs, bits=ic.core_opts.video_bits) #

Create an ISCC Video-Code with algorithm v0.

Parameters:

Name Type Description Default
frame_sigs ic.FrameSig

Sequence of MP7 frame signatures

required
bits int

Bit-length resulting Video-Code (multiple of 64)

ic.core_opts.video_bits

Returns:

Type Description
dict

ISCC object with Video-Code

Source code in iscc_core\code_content_video.py
def gen_video_code_v0(frame_sigs, bits=ic.core_opts.video_bits):
    # type: (Sequence[ic.FrameSig], int) -> dict
    """
    Create an ISCC Video-Code with algorithm v0.

    :param ic.FrameSig frame_sigs: Sequence of MP7 frame signatures
    :param int bits: Bit-length resulting Video-Code (multiple of 64)
    :return: ISCC object with Video-Code
    :rtype: dict
    """
    digest = soft_hash_video_v0(frame_sigs, bits=bits)
    video_code = ic.encode_component(
        mtype=ic.MT.CONTENT,
        stype=ic.ST_CC.VIDEO,
        version=ic.VS.V0,
        bit_length=bits,
        digest=digest,
    )
    iscc = "ISCC:" + video_code
    return dict(iscc=iscc)

ISO 24138 / 5.7 Mixed-Code#

gen_mixed_code_v0(codes, bits=ic.core_opts.mixed_bits) #

Create an ISCC Content-Code-Mixed with algorithm v0.

If the provided codes are of mixed length they are stripped to bits length for calculation.

Parameters:

Name Type Description Default
codes Iterable[str]

a list of Content-Codes.

required
bits int

Target bit-length of generated Content-Code-Mixed.

ic.core_opts.mixed_bits

Returns:

Type Description
dict

ISCC object with Content-Code Mixed.

Source code in iscc_core\code_content_mixed.py
def gen_mixed_code_v0(codes, bits=ic.core_opts.mixed_bits):
    # type: (Sequence[str], int) -> dict
    """
    Create an ISCC Content-Code-Mixed with algorithm v0.

    If the provided codes are of mixed length they are stripped to `bits` length for
    calculation.

    :param Iterable[str] codes: a list of Content-Codes.
    :param int bits: Target bit-length of generated Content-Code-Mixed.
    :return: ISCC object with Content-Code Mixed.
    :rtype: dict
    """
    digests = [ic.decode_base32(ic.iscc_clean(code)) for code in codes]
    digest = soft_hash_codes_v0(digests, bits=bits)
    mixed_code = ic.encode_component(
        mtype=ic.MT.CONTENT,
        stype=ic.ST_CC.MIXED,
        version=ic.VS.V0,
        bit_length=bits,
        digest=digest,
    )
    iscc = "ISCC:" + mixed_code
    return dict(iscc=iscc, parts=list(codes))

ISO 24138 / 5.8 Data-Code#

gen_data_code_v0(stream, bits=ic.core_opts.data_bits) #

Create an ISCC Data-Code with algorithm v0.

Parameters:

Name Type Description Default
stream Stream

Input data stream.

required
bits int

Bit-length of ISCC Data-Code (default 64).

ic.core_opts.data_bits

Returns:

Type Description
dict

ISCC object with Data-Code

Source code in iscc_core\code_data.py
def gen_data_code_v0(stream, bits=ic.core_opts.data_bits):
    # type: (ic.Stream, int) -> dict
    """
    Create an ISCC Data-Code with algorithm v0.

    :param Stream stream: Input data stream.
    :param int bits: Bit-length of ISCC Data-Code (default 64).
    :return: ISCC object with Data-Code
    :rtype: dict
    """

    hasher = DataHasherV0()
    data = stream.read(ic.core_opts.io_read_size)

    while data:
        hasher.push(data)
        data = stream.read(ic.core_opts.io_read_size)

    data_code = hasher.code(bits=bits)
    iscc = "ISCC:" + data_code
    return dict(iscc=iscc)

ISO 24138 / 5.9 Instance-Code#

gen_instance_code_v0(stream, bits=ic.core_opts.instance_bits) #

Create an ISCC Instance-Code with algorithm v0.

Parameters:

Name Type Description Default
stream Stream

Binary data stream for Instance-Code generation

required
bits int

Bit-length of resulting Instance-Code (multiple of 64)

ic.core_opts.instance_bits

Returns:

Type Description
dict

ISCC object with Instance-Code and properties: datahash, filesize

Source code in iscc_core\code_instance.py
def gen_instance_code_v0(stream, bits=ic.core_opts.instance_bits):
    # type: (ic.Stream, int) -> dict
    """
    Create an ISCC Instance-Code with algorithm v0.

    :param Stream stream: Binary data stream for Instance-Code generation
    :param int bits: Bit-length of resulting Instance-Code (multiple of 64)
    :return: ISCC object with Instance-Code and properties: datahash, filesize
    :rtype: dict
    """
    hasher = InstanceHasherV0()
    data = stream.read(ic.core_opts.io_read_size)
    while data:
        hasher.push(data)
        data = stream.read(ic.core_opts.io_read_size)

    instance_code = hasher.code(bits=bits)
    iscc = "ISCC:" + instance_code
    instance_code_obj = dict(
        iscc=iscc,
        datahash=hasher.multihash(),
        filesize=hasher.filesize,
    )

    return instance_code_obj

ISO 24138 / 6.0 ISCC-CODE#

gen_iscc_code_v0(codes) #

Combine multiple ISCC-UNITS to an ISCC-CODE with a common header using algorithm v0.

Parameters:

Name Type Description Default
codes Sequence[str]

A valid sequence of singluar ISCC-UNITS.

required

Returns:

Type Description
dict

An ISCC object with ISCC-CODE

Source code in iscc_core\iscc_code.py
def gen_iscc_code_v0(codes):
    # type: (Sequence[str]) -> dict
    """
    Combine multiple ISCC-UNITS to an ISCC-CODE with a common header using
    algorithm v0.

    :param Sequence[str] codes: A valid sequence of singluar ISCC-UNITS.
    :return: An ISCC object with ISCC-CODE
    :rtype: dict
    """

    codes = [ic.iscc_clean(code) for code in codes]

    # Check basic constraints
    if len(codes) < 2:
        raise ValueError("Minimum two ISCC units required to generate valid ISCC-CODE")
    for code in codes:
        if len(code) < 16:
            raise ValueError(f"Cannot build ISCC-CODE from units shorter than 64-bits: {code}")

    # Decode units and sort by MainType
    decoded = sorted(
        [ic.decode_header(ic.decode_base32(code)) for code in codes], key=itemgetter(0)
    )
    main_types = tuple(d[0] for d in decoded)
    if main_types[-2:] != (ic.MT.DATA, ic.MT.INSTANCE):
        raise ValueError(f"ISCC-CODE requires at least MT.DATA and MT.INSTANCE units.")

    # Determine SubType (generic mediatype)
    sub_types = [t[1] for t in decoded if t[0] in {ic.MT.SEMANTIC, ic.MT.CONTENT}]
    if len(set(sub_types)) > 1:
        raise ValueError(f"Semantic-Code and Content-Code must be of same SubType")
    st = sub_types.pop() if sub_types else ic.ST_ISCC.SUM if len(codes) == 2 else ic.ST_ISCC.NONE

    # Encode unit combination
    encoded_length = ic.encode_units(main_types[:-2])

    # Collect and truncate unit digests to 64-bit
    digest = b"".join([t[-1][:8] for t in decoded])
    header = ic.encode_header(ic.MT.ISCC, st, ic.VS.V0, encoded_length)

    code = ic.encode_base32(header + digest)
    iscc = "ISCC:" + code
    return dict(iscc=iscc)