open-standards/wai/extensions/iscc.md
Transaction Science fdf3243521 ISCC: the io.iscc.v0 soft binding in a C2PA store, matching on every code, the Meta-Code held to IEP-0002, and the Unicode tables pinned
c2pa-binding §4.9: a store of wai.asset.c2pa may carry the envelope's ISCC as the C2PA soft
binding io.iscc.v0 of IEP-0020, beside the identifier: the Image-Code of the envelope's picture
(where it is not featureless), the Data-Code and the Instance-Code of its bytes, with the
generator named in bindingMetadata. It has its own assertion shape and its own verify step: the
units are computed again from the envelope and the binding is held to them, each equal
(iscc_mismatch); it is the last of the soft bindings (manifest_malformed otherwise) and names no
robustness claim (soft_binding_unevidenced).

iscc §5: matching compares a binding's Data-, Text-, Audio-, Video- and Meta-Codes with the
asset's unit of the same type where the validator holds the input, under a threshold for each
type, the decision in integers; a pair of Meta-Codes is reported and never a match.

iscc §4.9: IEP-0002 was read. The meta element is read as a data URL (RFC 2397, base64) or a
JSON object in the JSON Canonicalization Scheme (RFC 8785), numbers as ECMA-262 writes them,
computed exactly; the text says where the IEP is silent or differs from the public vectors.

tools/wai_iscc_unicode_tables.py pins the SHA-256 of the tables it writes; --check and
tests/iscc.rs hold the committed tables to it, and CI runs both. No runner image carries a
Python of Unicode 17.0.0, so CI checks the pin; a Python that does also regenerates.

Corpora c2pa-identity/ (three good stores and eleven negatives more; the vectors it held keep
their bytes) and iscc/ are regenerated; c2pa_live_verify.py and iscc_verify.py compute all of it
again and drop each new check in turn.

Co-Authored-By: Claude <[email protected]>
2026-11-10 22:36:11 -05:00

604 lines
40 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WAI Extension: ISCC Content Identifiers
> Status: Draft. The International Standard Content Code of ISO 24138:2024 as WAI computes it from
> the standard's public texts: the codec of an ISCC (its header, its canonical form and the
> ISCC-SEQ of IEP-0020), the Instance-Code and the Data-Code of any bytes, the Image-Code of a
> picture, the Audio-Code of a Chromaprint fingerprint, the Video-Code of frame signatures, the
> Text-Code of a text, the Meta-Code of seed metadata, its meta element given as a data URL, as a
> JSON object or as bytes, the composite ISCC-CODE, and the matching of an ISCC soft binding against
> an asset, each code that this text computes compared with the asset's by the rule IEP-0020 gives
> its type. The Image-Code's transform is written out here, with a pinned
> table of cosines, so that every implementation that follows this text gets the same bits; the
> 32 × 32 picture it reads is WAI's own pre-processing of a decoded picture, in integers. Every
> code replays the standard's public test vectors. A store of [c2pa-binding](c2pa-binding.md)
> carries an envelope's codes as its ISCC identifier and as its `io.iscc.v0` soft binding (§4.9
> there). No record is registered and no
> transport changes. The reference implementation is the `iscc` module of `wai-rs` (feature
> `iscc`); the corpus is [`live-conformance/iscc/`](../live-conformance/iscc/), and its second
> implementation is `live-conformance/iscc_verify.py`.
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
>
> Texts read for this revision, each pinned to the version named, as fetched on 10 November 2026:
> the ISCC Enhancement Proposals that are the drafts contributed to ISO TC 46/SC 9/WG 18 for ISO
> 24138:2024 — IEP-0001 (*ISCC Structure and Format*, updated 2026-07-03), IEP-0002 (*Meta-Code*,
> updated 2026-01-19), IEP-0003 (*Text-Code*, updated 2026-01-19), IEP-0004 (*Image-Code*, updated 2026-10-21), IEP-0005 (*Audio-Code*,
> updated 2026-01-19), IEP-0006 (*Video-Code*), IEP-0008 (*Data-Code*), IEP-0009
> (*Instance-Code*) and IEP-0010 (*ISCC-CODE*), each updated 2026-01-19 — and IEP-0020 (*ISCC C2PA
> Conformance*, a draft updated 2026-10-28), which specifies the C2PA soft-binding algorithm
> `io.iscc.v0`; the conformance data of the ISCC reference implementation, version 1.5.0, a JSON
> file of test vectors whose SHA-256 is
> `1609adb921983baf83f9aaf049ab8dc23f828c214f57c29e2bf852346c678eff`; the C2PA soft-binding
> algorithm list's entry 3, `io.iscc.v0`; RFC 4648; and, for the Meta-Code's meta element, RFC
> 2397 (the data URL), RFC 8259, RFC 8785 (the JSON Canonicalization Scheme) and ECMA-262's
> *Number::toString*, which RFC 8785 §3.2.2.3 takes. For the constants the IEPs name and do
> not print, and for the steps of the Text-, Video-, Data- and Meta-Codes their prose leaves open,
> the source text of that reference implementation, version 1.5.0, was read as a specification
> and never run: Annex A names each file by its SHA-256 and states its licence. The text of
> ISO 24138:2024 itself was not read. A section number of an IEP in this text is that IEP's.
## 1. Scope
In scope: the header and the canonical form of an ISCC (§3.1, §3.2); the ISCC-SEQ (§3.3); the
Instance-Code (§4.1), the Image-Code (§4.2) and the picture it reads (§4.3), the Audio-Code (§4.4),
the ISCC-CODE (§4.5), the Data-Code (§4.6), the Text-Code (§4.7), the Video-Code (§4.8) and the
Meta-Code (§4.9); matching an ISCC soft binding against an asset (§5); and conformance (§6).
Out of scope: the Mixed-Code and the Semantic-Codes; an ISCC that holds one is read, carried and
compared as §5 says. Also out of scope: computing a Chromaprint fingerprint or an MPEG-7 video
signature from samples (the inputs IEP-0005 and IEP-0006 name), extracting text from a document,
a data URL whose data is not base64 and the canonical form of an XML meta element (§4.9),
embedding seed metadata in an asset and extracting it from one (IEP-0002, *Metadata embedding*
and *Metadata extraction*), the ISCC-ID (IEP-0011), and ISCC metadata (IEP-0012).
## 2. Terms
- **ISCC-UNIT**: an ISCC of one algorithm: MainType META (0), SEMANTIC (1), CONTENT (2), DATA (3)
or INSTANCE (4) (IEP-0001 §4.1).
- **ISCC-CODE**: an ISCC of MainType ISCC (5): headerless units composed under one header (§4.5).
- **ISCC-SEQ**: units, each with its header, concatenated (IEP-0020 §4.1).
- **Picture**: an image's 8-bit RGB samples, row-major; a grey sample stands for R = G = B.
- A digest named BLAKE3 is BLAKE3's 256-bit output; base32 is RFC 4648 §6's alphabet without
padding; base64 RFC 4648 §4's with padding.
## 3. The codec
### 3.1 The header
An ISCC is its **header** and its **body** (IEP-0001 §3). The header is four fields in order,
MainType, SubType, Version and Length, each an integer from 0 to 4679 written as nibbles: from 0
to 7 as one nibble `0xxx`; from 8 to 71 as `10` and six bits of the value less 8; from 72 to 583
as `110` and nine bits of the value less 72; from 584 to 4679 as `1110` and twelve bits of the
value less 584 (IEP-0001 Table 1). The nibbles are written high nibble first, and a header of an
odd number of nibbles ends with a zero nibble. A header is read only if its bytes are exactly the
encoding of the fields read: a padding nibble other than zero is refused.
The SubTypes of CONTENT, SEMANTIC and ISCC are TEXT (0), IMAGE (1), AUDIO (2), VIDEO (3) and
MIXED (4); an ISCC-CODE adds SUM (5) and NONE (6); META, DATA and INSTANCE have SubType 0. Every
Version is 0.
### 3.2 The canonical form
The body of a unit holds 32 × (Length + 1) bits, Length from 0 to 7 (IEP-0001 §4.4.1). The body
of an ISCC-CODE holds 128 bits and 64 more for each set bit of its Length, Length from 0 to 7
(§4.4.2 there). The **canonical form** of an ISCC is `ISCC:` and the base32 of its header and
body (§6.1 there); one is read only if it decodes to exactly one ISCC and its last character
carries no bit beyond the bytes.
### 3.3 The ISCC-SEQ
An ISCC-SEQ (IEP-0020 §4.1) is one or more units of MainType 0 to 4, each its header immediately
followed by its body, with nothing between, before or after them. Its order is not significant,
and a sequence may hold several units of one type. It is read from its first byte (§4.2 there):
the header at the offset, refused where the bytes end before it is complete (`header_truncated`)
or are not its fields' encoding (`header_not_canonical`); its MainType, refused above 4
(`main_type`); its Length, refused above 7 (`length`); its body, refused where fewer bytes remain
(`body_truncated`); then the next unit, until no byte remains. An empty sequence is refused
(`no_unit`). A unit of a SubType or Version this text does not name is read as it is, never
dropped or reordered.
## 4. The codes
### 4.1 The Instance-Code
The Instance-Code of bytes at *n* bits (a multiple of 32 from 32 to 256) is the unit (INSTANCE,
0, 0, *n*/32 − 1) whose body is the first *n*/8 bytes of the bytes' BLAKE3 digest (IEP-0009 §5).
Two equal Instance-Codes of 256 bits show the same bytes.
### 4.2 The Image-Code
The Image-Code of a 32 × 32 grey picture *p* (row-major, one byte a pixel) at *n* bits (64, 128,
192 or 256) is the unit (CONTENT, IMAGE, 0, *n*/32 − 1) whose body is (IEP-0004 §5):
1. the transform *M* of *p*: the transform below of each row, then of each column of the result,
every value a binary64 number;
2. for each square in this order, as many as *n*/64: the 8 × 8 values of *M* whose column and row
begin at (0, 0), (8, 0), (0, 8) and (8, 8), the quadrants of *M*'s first 16 × 16 (IEP-0004 §5,
*Clarification*): taken row by row, their **median** is the mean, (*a* + *b*) / 2, of the 32nd
and 33rd of them in ascending order, and each gives the bit 1 where it is greater than the
median and 0 otherwise;
3. the bits in that order, eight to a byte, the first bit the most significant.
The **transform** of a list *v* of length *N* (32, 16, 8, 4, 2 or 1) is the unscaled DCT-II,
*X*ₖ = Σₙ *v*ₙ cos(π (*n* + ½) *k* / *N*), computed by this recursion and no other, so that its
rounding is the same everywhere: for *N* = 1, *v*; otherwise, with *h* = *N*/2, *α*ᵢ = *v*ᵢ +
*v*_{N−1−i} and *β*ᵢ = (*v*ᵢ − *v*_{N−1−i}) / (2 *c*(*N*, *i*)) for *i* below *h*, their transforms
*A* and *B*, and the output *A*₀, *B*₀ + *B*₁, *A*₁, *B*₁ + *B*₂, …, *A*_{h−2}, *B*_{h−2} +
*B*_{h−1}, *A*_{h−1}, *B*_{h−1}. Every operation is binary64 arithmetic, rounded to nearest, in the
order written; *c*(*N*, *i*) is cos(π (*i* + ½) / *N*), taken from this table of binary64 bit
patterns rather than computed:
| *N* | *c*(*N*, 0) … *c*(*N*, *N*/2 − 1) |
|---|---|
| 32 | `3feff621e3796d7e 3fefa7557f08a517 3fef0a7efb9230d7 3fee212104f686e5 3feced7af43cc773 3feb728345196e3e 3fe9b3e047f38741 3fe7b5df226aafb0 3fe57d69348cec9f 3fe30ff7fce17036 3fe073879922ffed 3fdb5d1009e15cc2 3fd58f9a75ab1fdd 3fcf19f97b215f1e 3fc2c8106e8e613a 3fa91f65f10dd824` |
| 16 | `3fefd88da3d12526 3fee9f4156c62dda 3fec38b2f180bdb1 3fe8bc806b151741 3fe44cf325091dd6 3fde2b5d3806f63e 3fd294062ed59f05 3fb917a6bc29b438` |
| 8 | `3fef6297cff75cb0 3fea9b66290ea1a3 3fe1c73b39ae68c9 3fc8f8b83c69a60d` |
| 4 | `3fed906bcf328d46 3fd87de2a6aea964` |
| 2 | `3fe6a09e667f3bcd` |
Each is within one unit in the last place of the cosine. A constant row has, by this recursion,
every value but the first exactly zero, which IEP-0004's vectors of a uniform picture require.
### 4.3 The picture an Image-Code reads
IEP-0004 makes the Image-Code's behaviour normative only from the 32 × 32 grey picture (§6 there)
and leaves the way to it to the implementation. WAI's is a function of a decoded picture's samples
alone, in integers:
1. each pixel's grey is (77 R + 150 G + 29 B + 128) >> 8;
2. the picture of *W* × *H* (both at least 1) becomes 32 × 32 by area: output pixel (*x*, *y*) covers
the source area from *x* *W*/32 to (*x* + 1) *W*/32 across and from *y* *H*/32 to (*y* + 1) *H*/32
down; each source pixel weighs the area it shares with it, measured in thirty-seconds of a pixel
along each axis; and the output is the weighted sum of the greys plus ⌊*W H*/2⌋, divided by *W H*,
rounded down, the weights of every output pixel summing to *W H*.
IEP-0004 §5's steps turn a picture by its orientation tag, put a transparent one on white, crop
uniform borders and resize by bicubic interpolation. A WAI picture is decoded from a PNG without
an orientation tag or an alpha channel; this pre-processing crops nothing and resizes by area.
Another tool's 32 × 32 picture of the same picture may differ, and so may its Image-Code: the
Image-Code this text writes is reproduced by this pre-processing, and compared with another
tool's by similarity (§5).
### 4.4 The Audio-Code
The Audio-Code of a Chromaprint fingerprint *f*, a list of 32-bit signed integers, at *n* bits (a
multiple of 32 up to 256) is the unit (CONTENT, AUDIO, 0, *n*/32 − 1) whose body is the first
*n*/8 bytes of the concatenation of the 4-byte **hash** of (IEP-0005 §5): *f*; each of *f*'s four
consecutive quarters; and each of three consecutive thirds of *f* sorted in ascending order. A
list of *L* values is split into *k* parts of which the first *L* mod *k* hold ⌊*L*/*k*⌋ + 1
values and the rest ⌊*L*/*k*⌋. The hash of a list of *m* values, each read as its 32 bits, is the
32-bit word whose bit *b* is 1 where at least half of the values have bit *b* set (2 *c* ≥ *m*),
written most significant byte first; of no value, four zero bytes.
### 4.5 The ISCC-CODE
Units compose to an ISCC-CODE (IEP-0010 §6): at most one of each MainType, among them a Data-Code
and an Instance-Code, each of at least 64 bits, and a Semantic-Code and a Content-Code, where both
are given, of one SubType. In the order META, SEMANTIC, CONTENT, DATA, INSTANCE, each unit's first
64 bits make the body. The header is (ISCC, *S*, 0, *L*): *S* the Content-Code's SubType, else the
Semantic-Code's, else NONE where a Meta-Code is given, else SUM; *L*'s three bits, from the most
significant, a Meta-Code, a Semantic-Code and a Content-Code given.
### 4.6 The Data-Code
The Data-Code of bytes at *n* bits (a multiple of 32 from 32 to 256) is the unit (DATA, 0, 0,
*n*/32 − 1) whose body is the first *n*/8 bytes of the **minhash** of the **hash** of each
**chunk** of the bytes, in order (IEP-0008; the constants and the cut below as Annex A's source states them).
**The hash** of bytes is their XXH32 with seed 0 (the xxHash specification, *XXH32 algorithm
description*), a 32-bit number.
**The chunks.** The bytes are cut from their first byte into chunks, each beginning where the one
before ends. With the bytes that remain numbered from 0, *s* of them, and a pattern *p* that
starts at 0, the chunk's length is found by reading byte *i* from *i* = min(256, *s*):
1. while *i* < min(640, *s*): *p* ← ⌊*p*/2⌋ + *G*[byte *i*]; if *p* has none of its low 11 bits
set, the length is *i* + 1; otherwise *i* ← *i* + 1;
2. then while *i* < min(8192, *s*): the same, with the low 9 bits;
3. otherwise the length is *i*.
*G* is the gear table of Annex A.3, indexed by the byte's value; *p* stays below 2³². So a chunk
is at least 256 bytes and at most 8192, except the last. Bytes of length 0 are one chunk of no
bytes, whose hash is `02cc5d05`. The cut depends only on bytes at and before it, so cutting the
bytes as they arrive gives the same chunks.
**The minhash** of a list of 32-bit numbers *f* (not empty) is 256 bits. For *k* from 0 to 63,
*m*ₖ is the least, over the list, of (((*A*ₖ · *f* + *B*ₖ) mod 2⁶⁴) mod (2⁶¹ − 1)) mod 2³², with
*A* and *B* the tables of Annex A.1 and A.2. The bits are bit 0 of *m*₀ … *m*₆₃, then bit 1 of
each, then bit 2, then bit 3, eight to a byte, the first bit the most significant.
### 4.7 The Text-Code
The Text-Code of a text at *n* bits (a multiple of 32 from 32 to 256) is the unit (CONTENT, TEXT,
0, *n*/32 − 1) whose body is the first *n*/8 bytes of the minhash (§4.6) of the hash (§4.6) of the
UTF-8 of each **window** of 13 characters of the text's **collapse** (IEP-0003; the steps below as Annex A's source states them). Its
**characters** are the collapse's count of code points.
**The windows** of *w* items over a sequence are the runs of *w* consecutive items, each one item
on from the last, from the first item; a sequence of fewer than *w* items, one of none among them,
has one window, the whole sequence.
**The collapse** of a text is, in this order:
1. its Normalization Form D (the Unicode Standard, §3.11);
2. its lower case, by the Unicode Standard's default case conversion of a string (§3.13 there),
the final form of sigma included;
3. without every code point that is **white space** (general category Zs, or bidirectional class
WS, B or S) or whose general category begins with C, M or P; an unassigned code point is of
category Cn;
4. its Normalization Form KC.
**The Unicode version.** The source read takes these forms and categories from its language's
library, at whatever version of the Unicode Character Database that carries, and this text pins
none: two implementations agree on a text whose
characters have the same decompositions, combining classes, categories and lower case in the
versions they carry. The reference implementation's tables are of version 17.0.0, and its lower
case is its language's. The corpus's texts hold only characters assigned by Unicode 6.0.
### 4.8 The Video-Code
The Video-Code of one or more **frame signatures**, each a list of 380 non-negative integers (a
frame's signature in the MPEG-7 video signature IEP-0006 names), at *n* bits (a multiple of 32
from 32 to 256) is the unit (CONTENT, VIDEO, 0, *n*/32 − 1) whose body is (IEP-0006; the pairs and
the steps as Annex A's source states them): the signatures that differ from one another, each once, summed value by value into *v*₀ …
*v*₃₇₉; then, for the first *n* pairs (*a*, *b*) of Annex A.4 in order, the bit 0 where *v*ₐ ≥
*v*_b and 1 otherwise; eight bits to a byte, the first bit the most significant.
### 4.9 The Meta-Code
The Meta-Code of **seed metadata**, a **name**, a **description** and, optionally, a **meta
element**, at *n* bits (a multiple of 32 from 32 to 256), is the unit (META, 0, 0, *n*/32 − 1)
whose body is the first *n*/8 bytes of the digest below (IEP-0002). Where IEP-0002's prose leaves
a step open, or says other than the public vectors hold, the step is the one the vectors hold,
read from Annex A's source (`code_meta.py`); each such step says so.
**The meta element** is given in one of three forms, each read to a **payload** of bytes
(IEP-0002, *meta element*):
- A **data URL** (RFC 2397) whose data is base64: `data:`, a media type and its parameters, which
are not read, `;base64`, a comma, and the base64 of the payload. `data:` and `;base64` are
read without regard to the case of their ASCII letters, and the comma is the URL's first. The
base64 is read strictly: groups of four characters of RFC 4648 §4's alphabet, padding in the
last group only, and no bit set beyond the bytes. Any other text is refused
(`meta_url_malformed`); RFC 2397's other form, whose data is percent-encoded, is among them,
since IEP-0002 has the data base64.
- A **JSON object**, given as a JSON text (RFC 8259). Its payload is the object in the JSON
Canonicalization Scheme (RFC 8785): no white space; an object's members in the order of their
names' UTF-16 code units; a string escaped as §3.2.2.2 there has it; and a number its nearest
binary64 value as ECMA-262's *Number::toString* writes one (§3.2.2.3 there): the fewest
decimal digits that read back to that value; of several such, the nearest to it; of two equally
near, the one whose last digit is even. A member name given twice keeps its last value. The
object's data URL is `data:application/ld+json;base64,` where the object has a member named
`@context` and `data:application/json;base64,` otherwise, followed by the base64 of the payload
(the choice between IEP-0002's two media types is the source's). A text that is not JSON, one
with a number that has no finite binary64 value among them, is refused (`meta_json_malformed`),
and so is a JSON value that is not an object (`meta_json_not_object`).
- **Bytes**, which are the payload: this text's own entry, for a caller that holds them. Bytes
have no data URL, and a payload of no bytes is no meta element.
A data URL or a JSON object that gives no bytes is refused (`payload_empty`): IEP-0002 and the
public vectors say nothing of a meta element of no bytes, and the source read computes from it
a code the description does not enter, which is not what bytes of none compute here. An object
of no member is an object, and its payload is the two bytes `{}`; the source read takes it as no
meta element, which IEP-0002 does not say and no public vector holds. An XML meta
element is given as the data URL of its canonical form (IEP-0002, *meta element*, item 4), which
this text does not compute. A payload of more than 128,000 bytes is refused (`payload_too_long`),
the source's limit; IEP-0002 states none.
**Clean.** The clean of a text is: its Normalization Form KC; without every code point whose
general category begins with C, except the seven **line ends** U+000A, U+000B, U+000C, U+000D,
U+0085, U+2028 and U+2029; cut into lines at each line end, U+000D U+000A being one, a last line
end opening no line; each run of lines that are empty or hold only white space (§4.7) kept as one
empty line, which is how the source reads IEP-0002's "a maximum of two consecutive newlines"; the
lines joined by U+000A; white space removed from both ends.
**Trim.** The trim of a text at *b* bytes is the longest prefix of at most *b* bytes of its UTF-8
that ends on a character, white space removed from both ends.
1. The name read is the clean of the name, its runs of white space each replaced by one U+0020
and those at its ends removed, trimmed at 128 bytes (IEP-0002, *name element*; the one space
for a run is the vectors'). A Meta-Code needs a name that is then not empty (`name_empty`).
2. The description read is the clean of the description (IEP-0002, *description element*),
trimmed at 4096 bytes, the source's limit, which the vectors hold and IEP-0002 does not state.
3. The meta element, where one is given, is read to its payload.
4. *N* is the **similarity hash** of the BLAKE3 digests of the UTF-8 of each window (§4.7) of 3
characters of the collapse (§4.7) of the name read. The similarity hash of 256-bit digests is
the 256 bits whose bit *b* is 1 where at least half of them have bit *b* set.
5. With a payload, *E* is the similarity hash of the BLAKE3 digests of each window of 4 bytes of
the payload; without one and with a description read that is not empty, *E* is computed from
the description read as *N* is from the name; otherwise there is no *E*.
6. The digest is *N* where there is no *E*, and otherwise the first four bytes of *N*, the first
four of *E*, the next four of *N*, the next four of *E*, and so on through the first sixteen
bytes of each: 256 bits. IEP-0002's *Meta-Code processing*, item 5, has the second part made
from the name where there is no description; the public vectors of a name alone hold *N*
whole, not *N* interleaved with itself, and *N* whole is what this step computes.
The **metahash** is the bytes `1e 20` and the BLAKE3 digest of the payload, or without one of
the UTF-8 of the name read, U+0020 and the description read, white space removed from both ends
(IEP-0002, *Meta-Hash processing*). A description beside a meta element is read and enters
neither the digest nor the metahash.
What a Meta-Code states with its unit (IEP-0002, *Outputs*): the name read; the description
read, where it is not empty; the meta element's data URL, the one given, unaltered, or the one a
JSON object is written to; and the metahash.
## 5. Matching
An ISCC soft binding (IEP-0020 §4.3) is an ISCC-SEQ in a block of a C2PA soft-binding assertion of
algorithm `io.iscc.v0`. A validator matches one against an **asset**: the asset's bytes, and each
of these that it holds for the asset: its picture (§4.3), its text, its Chromaprint fingerprint,
its frame signatures and its seed metadata (§4.9). Matching (§6 there) takes each unit of the
binding of Version 0 and 256 bits, in the binding's order, and compares it with the unit of the
same MainType, SubType and length computed from the asset:
| the binding's unit | the asset's, computed from | compared |
|---|---|---|
| Instance-Code (INSTANCE, 0) | its bytes (§4.1) | for equality |
| Data-Code (DATA, 0) | its bytes (§4.6) | by similarity |
| Image-Code (CONTENT, IMAGE) | its picture (§4.2) | by similarity, unless either is featureless |
| Text-Code (CONTENT, TEXT) | its text (§4.7) | by similarity |
| Audio-Code (CONTENT, AUDIO) | its fingerprint (§4.4) | by similarity |
| Video-Code (CONTENT, VIDEO) | its frame signatures (§4.8) | by similarity |
| Meta-Code (META, 0) | its seed metadata (§4.9) | by similarity, and never a match |
Every other unit is ignored: one of another Version or length; one of a type this text does not
compute (a Semantic-Code, a Mixed-Code, a SubType §3.1 does not name for its MainType); and one
whose input the validator does not hold for the asset, or whose seed metadata gives no Meta-Code.
A unit is compared with the asset's unit of its own type and with no other: the bits of a
Text-Code beside those of a Data-Code say nothing of the asset.
**Similarity.** The similarity of two bodies of *n* bits that differ in *d* is *S* =
⌊100 (*n* − *d*) / *n*⌋, an integer from 0 to 100 (§6 there).
**Featureless.** An Image-Code is featureless where at least one complete 64-bit segment of its
body has fewer than eight bits set (§3 there); a pair of Image-Codes either of which is
featureless is not compared.
**Thresholds.** A validator holds a threshold *T*, an integer from 0 to 100, for each type that
can be a candidate match: the Text-Code, the Image-Code, the Audio-Code, the Video-Code and the
Data-Code. A validator with none of its own uses 80 for each (§6 there; 51 differing bits of 256
at most). A pair is:
- an **exact match**, where it is of Instance-Codes and the bodies are equal;
- a **candidate match**, where it is of one of those five types and 100 (*n* − *d*) ≥ *T* *n* for
its type's *T*, which is *S* ≥ *T*. The comparison is of integers: nothing is rounded, and no
fraction is formed.
A pair of Meta-Codes is compared and its similarity reported, and it is no match at any
similarity: IEP-0020 names a candidate match of a Semantic-Code, a Content-Code or a Data-Code
(§3 there), and a Meta-Code has no threshold.
The binding matches where a pair is an exact or a candidate match. Its **score** is 100 for an
exact match, and otherwise the highest similarity of a compared pair of Content-Codes or
Data-Codes, at most 99; with none compared, there is no score. A match reports each pair
compared, in the binding's order, with its similarity and whether it is a match, and whether the
Instance-Codes are equal. A similar code shows similar content, not the same content; only the
Instance-Code shows the same bytes (§8 there).
## 6. Conformance
[`live-conformance/iscc/`](../live-conformance/iscc/) holds:
- `public.json`, the public test vectors, transcribed and not generated, whose SHA-256 is
`78692c10eb7d8551b92d8a0c650b4c201bd4f137e2055f9f4a4db4e11f45926d`: the cases of the reference
implementation's conformance data, version 1.5.0, for the Image-Code from 32 × 32 grey pictures,
the Audio-Code from Chromaprint fingerprints, the Instance-Code and the Data-Code from bytes, the
Video-Code from frame signatures, the Text-Code from texts, the Meta-Code from names,
descriptions and meta elements (each meta element as the reference's data gives it, a data URL
or the text of a JSON object, with the payload and the data URL its output states), the
ISCC-CODE from units, and the
ISCC-SEQ's encoding and decoding; and IEP-0020 §10.1's sequence with its canonical form and
base64, Table 2's sequences a decoder refuses, the sequence it accepts of a SubType ISO 24138
does not define, and Table 3's featureless Image-Codes;
- `vectors.json` and `pictures/`, written from fixed seeds by `wai_iscc_vectors gen`: pictures
the generator draws, among them sizes prime to 32, a strip, a single pixel and a picture smaller
than 32 each way, each with the SHA-256 of its 32 × 32 picture (§4.3), its Image-Code and
whether it is featureless; soft bindings matched against assets (§5), each case stating its
asset's bytes (a picture's file, or a buffer by length and seed with the bytes at stated
offsets complemented) and the inputs the validator holds (a text, a fingerprint, frame
signatures by count and seed, seed metadata), under the thresholds the file states or the
case's own: the same picture, the picture at half size, the picture after one pixel of it was
altered (no exact match), another picture, a featureless binding, units that are ignored, every
code against the asset it was computed from, the same units against an asset whose inputs are
not held, for each of the five types a unit just inside its threshold (the asset's own with 51
bits flipped, 80 of 100) and one just outside (52 bits, 79), a text reworded and another text,
a buffer with three bytes changed and another buffer, a Meta-Code equal to the asset's (no
match) and one of a changed name, each body under another type's header (none near), and
thresholds of a validator's own, one type's stricter than another's; buffers by length
and seed, around the least chunk, the centre and the greatest chunk of §4.6, each with its
chunks' lengths and its Data-Code; frame signatures by count and seed, a frame repeated, each
with its Video-Code; texts with their collapse, characters and Text-Code, among them accents, a
final sigma, Hangul, compatibility characters, combining marks out of order, a composition
exclusion and controls; names, descriptions and meta elements with what §4.9 reads of them,
their payload, their data URL, their metahash and their Meta-Code, among them data URLs with
parameters and in upper case, and JSON objects with members to sort, numbers at each of
*Number::toString*'s forms, a tie between two digits, and strings to escape; and meta elements
§4.9 refuses, each with its reason. Byte *i* of the buffer of seed *s* is bits 16 to 23 of
*x*ᵢ₊₁, with *x*₀ = *s* and *x*ₖ₊₁ = (1103515245 *x*ₖ + 12345) mod 2³¹, and seed 0 is the
buffer of zero bytes; value *j* of frame *f* of seed *s* is (*j* (*f* + 1) + *s*) mod 3, and the
frames are followed by the first frame again; value *i* of the fingerprint of seed *s* is bytes
4*i* to 4*i* + 3 of the buffer of seed *s*, most significant first, as a signed integer.
A conforming implementation gives every case the code, the units, the refusal or the match the
corpus states. `wai_iscc_vectors verify` and the test `tests/iscc.rs` check that every generated
file regenerates byte for byte, that no other file but `README.md` and `public.json` is there,
that every public vector replays, that every case gets its outcome, and that `public.json` is the
file this section pins. `live-conformance/iscc_verify.py` is the second implementation, written
from this text and the texts it reads: it shares no code with `wai-rs`; it decodes the PNGs with
an inflater of its own, hashes with BLAKE3 from `claim-conformance/_blake3.py`, reads Annex A's
tables from this text, takes its Unicode data from its own language's library, replays every
public vector, gives every case the same outcome, and requires every mutated copy of a case to be
refused or to change its outcome.
## 7. Security and privacy considerations
- **Similar is not the same.** Only the Instance-Code is a cryptographic hash. Unrelated pictures
can have similar Image-Codes, a featureless one most of all, which is why it is never compared,
and content can be altered until its codes come near another's (IEP-0020 §8). A candidate match
is a lead for a check suited to the content, never a binding. A Data-Code is no weaker a lead
and no stronger: bytes changed in a few places keep most of their chunks, and so their
Data-Code, while their Instance-Code is another.
- **Metadata is anyone's to write.** A Meta-Code is computed from a name and a description that
whoever writes them chooses, so two assets are given equal Meta-Codes at no cost. §5 reports a
pair of Meta-Codes and never matches on one.
- **A meta element is data.** §4.9 decodes a data URL's base64 and canonicalises a JSON object;
it follows no reference, fetches nothing, and reads no media type.
- **A copied code.** An Instance-Code in a store is public, and anyone can copy it into another
store. The ISCC soft binding and the ISCC identifier of [c2pa-binding](c2pa-binding.md) §4.9
are signed into their store's claim and computed again from the envelope the store binds, each
unit held to equality, so a copied code does not verify beside other bytes, and neither does
one that is only near.
- **Pre-processing.** An Image-Code is as stable as the 32 × 32 picture it reads. Two tools that
pre-process one picture differently can give codes further apart than two pictures give one
tool; a party matching across tools sets its threshold for that.
- **Privacy.** A code identifies content, not a person. It can tell that two copies are the same
or alike, which is what it is for: publishing a code of content publishes that ability.
## Annex A. Tables of the Data-, Text- and Video-Codes
The IEPs name these constants and leave their values to the reference implementation's source.
They are transcribed here from the source text of `iscc-core`, version 1.5.0 (its tag `v1.5.0`),
as fetched on 10 November 2026. That source was read and never imported, built or run.
| table | file | SHA-256 of the file |
|---|---|---|
| A.1, A.2 | `iscc_core/minhash.py` (`MPA`, `MPB`) | `4e812133d9d8d8a126091895baa08e54f56c865c670333589fbf1c3453f11fd6` |
| A.3 | `iscc_core/options.py` (`cdc_gear`) | `034d4a622894714850a2a41c689abcc5b52dc884e3ac7d6d89f21c65afa5e3a9` |
| A.4 | `iscc_core/wtahash.py` (`WTA_VIDEO_ID_PERMUTATIONS`) | `b1fb110940fa5b33003e51b8e63f1abd31229fd9d2ff80635973452aef73511a` |
The steps of §4.6 to §4.9 that the IEPs' prose leaves open were read from `iscc_core/cdc.py`
(`7c23e514e15a14dd479f57b3f0615d4ea18988de7c14390df7e5d4ca7813f680`), `code_data.py`
(`4f744c918c941ff4753517568b687257131ff1110f9157af2639a49d3c34a002`), `code_content_text.py`
(`cd5e228228bd093171e8509e46bd19e1d88e3565c0fa0a23bec5fc03cd8780c7`), `code_content_video.py`
(`ca409037f94b89b69c7afad57fad46796edbe202731978a77b8229d225ee4dcd`), `code_meta.py`
(`1d48d165f8e9ec1a4dbda4802697fa997b7bacb4fa73987201c11558dea3c81f`), `simhash.py`
(`d4bb145f5582bcf7009fe5db3e073d7b79b0737b12f201aa4ded99738d73b75f`) and `utils.py`
(`b89d66ba72481f0232204593eb12992f8b38d7a297a4ab8f7c78d1b2643e9a83`), and are written here in
this text's own words; no code of those files is in this repository.
**Licence (decided).** `iscc-core` is Copyright 2016-2026 Titusz Pan and is licensed under the
Apache License, Version 2.0, the licence of this text. The tables below are reproduced under it,
with this notice as its attribution; the reading recorded here is that constants an
interoperable implementation of a standard must hold may be reproduced under that licence with
attribution, and that describing an algorithm in one's own words reproduces no code. The Unicode
data the reference implementation of this text carries (§4.7) is derived from the Unicode
Character Database, Copyright © 1991-2025 Unicode, Inc., under the Unicode License v3.
Each value is hexadecimal, most significant digit first, in the table's order.
### A.1 The minhash multipliers *A*₀ … *A*₆₃
```text
0bd6fc3666cddccd 19aa265a296861b9 0fb49116249f240b 1adfc726aa0e5561
0c5ce3a5a06b706b 1b97ae3504f68d05 138d6618323c0103 1db3b398a8a09af9
185b745ad0bfcc4d 12d12f23cf0e9aad 0795dd9577a978b3 06e7d7fc0f99e34d
1bc3f30e608b1f5d 0ead57b0906151af 15dfcd6ab8a2fefd 1cd9533f55cc8673
0e31107120a54313 1dd6a07fbce3ea19 09e2be8079dfdf2f 14fad77228bf5eaf
152cebf702e35f9d 1189d1c0ba059a33 10dff879fb1c38af 083058c40f70e60b
01d0c85297804a39 0d5c4995355da7b7 17a0b681612e861f 041bddefc7647a75
06cd94739c33f043 116bd16f547bd8ff 09b4fa40e2209827 0074fdd0712066b5
1b8c7bf89b896469 05062e675f96b6db 1a69b5bdbb8eed2b 0c7ed59cd4700c29
0799012d426d7c6f 1771dc7389d3dacf 0bbc27794a831943 058e43cdca23150d
026d9ba8744b8015 0c2a00912496855b 0705226d8016da9d 1aa6dfeeea108247
11f04783ff3d1e6d 0d5e63ecea7ccc9f 1805523801e53327 03f327c54f7c4bc5
10720f1822703779 1e13c1b3d5491a6d 10dc35f27d79d913 15955e73e37ffb71
0e8b4adcc1633f0b 1130750cadb338d5 10d73a2b670cfd6f 03f925002144e9df
115a2a7110219dc9 05a7dabd33c9c463 0d55b1668de1a12b 17e8426ee18e595f
1265d8fa972717bf 02bbd023752576d7 172aee6666d154b3 0ad7dd83733f8a95
```
### A.2 The minhash addends *B*₀ … *B*₆₃
```text
0f1f0fc99887ea6b 0a16b994dc3e2e31 00eb34b84804bce1 0ef748584001dd8c
12a4f6f418028b4b 09b21d5014ea68a7 115383e3e873be27 141832af19da3c79
0e5ba22c9b8b9597 1474bad6b504b08e 0b040cc67987e745 0d9b708e8e83c66f
0d609bbd004e3c77 033e191007d3a85d 0362866a7c0a8dd3 07ea010bad227ece
042167e53cd07b2c 150cbece2d2bc69d 14be94be75539629 17fdb8f10970bd53
0e4871b571e49530 170c1b6091d92ea8 136cfb07a4630835 070753a97f8d2789
11c27ab17baf8edd 0befad23100a6f74 05b602f9333e9d8c 00f70d86a9d1b347
1b350f0580186b99 0865b809757aca74 0e1edf3c5eabb21d 121bb418ab60509c
0d2b92c2a4ee995b 0fdbf4f00758271b 0801d841df2f5d40 0eddec5e885fb480
14bda0b2c901d2eb 0ee5184f15aac6aa 11d8730eb8699e99 11b9715b4fb518d7
04c561c0cf35cca1 1d27f34e80cc26e3 1109b4e108174f4a 00420e62e60aeef1
1d2b647f2464f369 02a9709cb76f3ea8 1e1dd51c3d760398 0cbeeba5a7d69dfd
19691b22e0998389 1b14a4990f3ff12e 0a547884667ffce3 1aaa9ab9597b6f22
1c0911f4404a97d6 187e4a9d069783c9 1a6bfcefede6406c 141d3a7ba317ccca
13ed1387613eaca9 0b901cc1caa732fd 1f127e7dfd0015af 0a64d32f76a735be
1975c032c417f355 0314bf0108540316 0c81d79ae5775341 14d6cb5e18ae5b4e
```
### A.3 The gear table *G*, by byte value 0 … 255
```text
5c95c078 22408989 2d48a214 12842087 530f8afb 474536b9 2963b4f1 44cb738b
4ea7403d 4d606b6e 074ec5d3 3af39d18 726003ca 37a62a74 51a2f58e 7506358e
5d4ab128 4d4ae17b 41e85924 470c36f7 4741cbe1 01bb7f30 617c1de3 2b0c3a1f
50c48f73 21a82d37 6095ace0 419167a0 3caf49b0 40cea62d 66bc1c66 545e1dad
2bfa77cd 6e85da24 5fb0bdc5 652cfc29 3a0ae1ab 2837e0f3 6387b70e 13176012
4362c2bb 66d8f4b1 37fce834 2c9cd386 21144296 627268a8 650df537 2805d579
3b21ebbd 7357ed34 3f58b583 7150ddca 7362225e 620a6070 2c5ef529 7b522466
768b78c0 4b54e51e 75fa07e5 06a35fc6 30b71024 1c8626e1 296ad578 28d7be2e
1490a05a 7cee43bd 698b56e3 09dc0126 4ed6df6e 02c1bfc7 2a59ad53 29c0e434
7d6c5278 507940a7 5ef6ba93 68b6af1e 46537276 611bc766 155c587d 301ba847
2cc9dda7 0a438e2c 0a69d514 744c72d3 4f326b9b 7ef34286 4a0ef8a7 6ae06ebe
669c5372 12402dcb 5feae99d 76c7f4a7 6abdb79c 0dfaa038 20e2282c 730ed48b
069dac2f 168ecf3e 2610e61f 2c512c8e 15fb8c06 5e62bc76 69555135 0adb864c
4268f914 349ab3aa 20edfdb2 51727981 37b4b3d8 5dd17522 6b2cbfe4 5c47cf9f
30fa1ccd 23dedb56 13d1f50a 64eddee7 0820b0f7 46e07308 1e2d1dfd 17b06c32
250036d8 284dbf34 68292ee0 362ec87c 087cb1eb 76b46720 104130db 71966387
482dc43f 2388ef25 524144e1 44bd834e 448e7da3 3fa6eaf9 3cda215c 3a500cf3
395cb432 5195129f 43945f87 51862ca4 56ea8ff1 201034dc 4d328ff5 7d73a909
6234d379 64cfbf9c 36f6589a 0a2ce98a 5fe4d971 03bc15c5 44021d33 16c1932b
37503614 1acaf69d 3f03b779 49e61a03 1f52d7ea 1c6ddd5c 062218ce 07e7a11a
1905757a 7ce00a53 49f44f29 4bcc70b5 39feea55 5242cee8 3ce56b85 00b81672
46beeccc 3ca0ad56 2396cee8 78547f40 6b08089b 66a56751 781e7e46 1e2cf856
3bc13591 494a4202 520494d7 2d87459a 757555b6 42284cc1 1f478507 75c95dff
35ff8dd7 4e4757ed 2e11f88c 5e1b5048 420e6699 226b0695 4d1679b4 5a22646f
161d1131 125c68d9 1313e32e 4aa85724 21dc7ec1 4ffa29fe 72968382 1ca8eef3
3f3b1c28 39c2fb6c 6d76493f 7a22a62e 789b1c2a 16e0cb53 7deceeeb 0dc7e1c6
5c75bf3d 52218333 106de4d6 7dc64422 65590ff4 2c02ec30 64a9ac67 59cab2e9
4a21d2f3 0f616e57 23b54ee8 02730aaa 2f3c634d 7117fc6c 01ac6f05 5a9ed20c
158c4e2a 42b699f0 0c7c14b3 02bd9641 15ad56fc 1c722f60 7da1af91 23e0dbcb
0e93e12b 64b2791d 440d2476 588ea8dd 4665a658 7446c418 1877a774 5626407e
7f63bd46 32d2dbd8 3c790f4a 772b7239 6f8b2826 677ff609 0dc82c11 23ffe354
2eac53a6 16139e09 0afd0dbc 2a4d4237 56a368c7 234325e4 2dce9187 32e8ea7e
```
### A.4 The 256 pairs of the Video-Code
Each pair is written *a*,*b* in decimal: two indices into a frame signature.
```text
292,16 219,247 295,7 105,236 251,142 334,82 17,266 250,167
38,127 184,22 215,71 308,181 195,215 145,345 134,233 89,351
155,338 185,68 233,122 225,314 192,22 298,2 120,68 99,155
274,187 122,160 341,281 230,223 240,33 334,299 166,256 80,114
211,122 18,16 254,154 310,336 36,273 41,76 196,290 191,307
76,57 49,226 85,97 178,221 212,228 125,348 140,73 316,267
91,61 136,233 154,84 338,332 89,90 245,177 167,222 114,2
278,364 22,169 163,124 40,134 229,207 298,81 199,253 344,123
376,268 139,266 247,308 255,32 85,250 345,236 205,69 215,277
299,178 275,198 250,359 84,286 225,50 212,18 1,224 274,33
25,179 47,77 55,311 232,248 71,234 223,256 228,175 371,132
357,234 216,168 332,266 267,78 378,121 165,316 16,351 100,329
301,294 321,245 12,59 151,222 126,367 148,45 23,305 281,54
146,83 343,244 72,184 304,205 98,179 93,40 302,99 218,106
49,350 157,237 355,267 369,216 229,340 284,106 136,305 186,59
3,107 217,312 209,195 333,102 35,216 45,28 178,130 184,233
217,99 321,144 238,355 150,259 255,259 134,207 226,327 174,178
371,141 247,228 244,300 245,42 353,276 368,187 369,207 86,308
212,368 288,33 304,375 156,8 302,167 333,164 37,379 203,312
191,144 310,95 123,86 157,48 284,27 112,291 37,215 98,291
292,224 303,8 200,103 173,294 97,267 288,167 24,336 354,296
25,18 289,187 203,166 307,326 87,80 60,310 176,84 15,370
274,261 178,45 203,224 295,178 30,74 227,361 241,312 231,369
226,309 89,181 216,175 286,262 234,198 99,49 221,328 78,21
95,327 324,97 291,219 184,286 192,25 309,26 84,159 114,25
296,90 51,325 289,184 95,154 21,202 306,219 39,176 99,251
83,86 207,239 168,19 88,90 297,361 215,78 262,328 356,200
48,203 60,120 54,216 369,327 159,370 148,273 332,50 176,267
317,243 311,125 272,148 6,340 80,346 197,355 117,49 261,326
242,51 295,204 298,111 147,181 35,96 318,285 271,13 38,204
16,8 334,220 173,91 372,24 183,166 320,243 87,9 105,65
148,103 197,314 279,299 304,214 282,15 64,2 63,14 28,351
```