RDF Dataset Canonicalization

A Standard RDF Dataset Canonicalization Algorithm

W3C Working Draft

More details about this document
This version:
https://www.w3.org/TR/2023/WD-rdf-canon-20230607/
Latest published version:
https://www.w3.org/TR/rdf-canon/
Latest editor's draft:
https://w3c.github.io/rdf-canon/spec/
History:
https://www.w3.org/standards/history/rdf-canon
Commit history
Test suite:
https://w3c.github.io/rdf-canon/tests/
Editors:
Dave Longley (Digital Bazaar)
Gregg Kellogg
Dan Yamamoto
Former editor:
Manu Sporny (Digital Bazaar) (CG Report)
Author:
Dave Longley (Digital Bazaar)
Feedback:
GitHub w3c/rdf-canon (pull requests, new issue, open issues)
public-rch-wg@w3.org with subject line [rdf-canon] … message topic … (archives)

Abstract

RDF [RDF11-CONCEPTS] describes a graph-based data model for making claims about the world and provides the foundation for reasoning upon that graph of information. At times, it becomes necessary to compare the differences between sets of graphs, digitally sign them, or generate short identifiers for graphs via hashing algorithms. This document outlines an algorithm for normalizing RDF datasets such that these operations can be performed.

Status of This Document

This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.

This document describes the URDNA2015 algorithm for canonicalizing RDF datasets, which was the input from the W3C Credentials Community Group published as [CCG-RDC-FINAL]. There are other canonicalization algorithms actively being considered by the Working Group – notably [Hogan-Canonical-RDF]; future versions of this document may change accordingly. See Issue 6: Compare the two algorithms, and decide on basis for our work and Issue 10: C14N choice criteria for further discussion.

At the time of publication, [RDF11-CONCEPTS] is the most recent recommendation defining RDF datasets and [N-QUADS], however work on an updated specification is ongoing within the W3C RDF-star Working Group. Some dependencies from relevant updated specifications are provided normatively in this specification with the expectation that a future update to this specification will replace those with normative references to updated RDF specifications.

This document was published by the RDF Dataset Canonicalization and Hash Working Group as a Working Draft using the Recommendation track.

Publication as a Working Draft does not imply endorsement by W3C and its Members.

This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 2 November 2021 W3C Process Document.

1. Introduction

This section is non-normative.

When data scientists discuss canonicalization, they do so in the context of achieving a particular set of goals. Since the same information may sometimes be expressed in a variety of different ways, it often becomes necessary to transform each of these different ways into a single, standard representation. With a standard representation, the differences between two different sets of data can be easily determined, a cryptographically-strong hash identifier can be generated for a particular set of data, and a particular set of data may be digitally-signed for later verification.

In particular, this specification is about normalizing RDF datasets, which are collections of graphs. Since a directed graph can express the same information in more than one way, it requires canonicalization to achieve the aforementioned goals and any others that may arise via serendipity.

Most RDF datasets can be normalized fairly quickly, in terms of algorithmic time complexity. However, those that contain nodes that do not have globally unique identifiers pose a greater challenge. Normalizing these datasets presents the graph isomorphism problem, a problem that is believed to be difficult to solve quickly. More formally, it is believed to be an NP-Intermediate problem, that is, neither known to be solvable in polynomial time nor NP-complete. Fortunately, existing real world data is rarely modeled in a way that manifests this problem and new data can be modeled to avoid it. In fact, software systems can detect a problematic dataset and may choose to assume it's an attempted denial of service attack, rather than a real input, and abort.

This document outlines an algorithm for generating a canonical serialization of an RDF dataset given an RDF dataset as input. The algorithm is called the Universal RDF Dataset Canonicalization Algorithm 2015 or URDNA2015.

1.1 Uses of Dataset Canonicalization

There are different use cases where graph or dataset canonicalization are important:

A canonicalization algorithm is necessary, but not necessarily sufficient, to handle many of these use cases. The use of blank nodes in RDF graphs and datasets has a long history and creates inevitable complexities. Blank nodes are used for different purposes:

Furthermore, RDF semantics dictate that deserializing an RDF document results in the creation of unique blank nodes, unless it can be determined that on each occasion, the blank node identifies the same resource. This is due to the fact that blank node identifiers are an aspect of a concrete RDF syntax and are not intended to be persistent or portable. Within the abstract RDF model, blank nodes do not have identifiers (although some RDF store implementations may use stable identifiers and may choose to make them portable). See Blank Nodes in [RDF11-CONCEPTS] for more information.

RDF does have a provision for allowing blank nodes to be published in an externally identifiable way through the use of Skolem IRIs, which allow a given RDF store to replace the use of blank nodes in a concrete syntax with IRIs, which then serve to repeatably identify that blank node within that particular RDF store; however, this is not generally useful for talking about the same graph in different RDF stores, or other concrete representations. In any case, a stable blank node identifier defined for one RDF store or serialization is arbitrary, and typically not relatable to the context within which it is used.

This specification defines an algorithm for creating stable blank node identifiers repeatably for different serializations possibly using individualized blank node identifiers of the same RDF graph (dataset) by grounding each blank node through the nodes to which it is connected. As a result, a graph signature can be obtained by hashing a canonical serialization of the resulting normalized dataset, allowing for the isomorphism and digital signing use cases. As blank node identifiers can be stable even with other changes to a graph (dataset), in some cases it is possible to compute the difference between two graphs (datasets), for example if changes are made only to ground triples, or if new blank nodes are introduced which do not create an automorphic confusion with other existing blank nodes. If any information which would change the generated blank node identifier, a resulting diff might indicate a greater set of changes than actually exists. Additionally, if the starting dataset is an N-Quads document, it may be possible to correlate the original blank node identifiers used within that N-Quads document with those issued in the normalized dataset.

Editor's note

Add descriptions for relevant historical discussions and prior art:

[DesignIssues-Diff]
TimBL's design note on problems with Diff.
[eswc2014Kasten]
A Framework for Iterative Signing of Graph Data on the Web.
[Hogan-Canonical-RDF]
Aiden Hogan's paper on canonicalizing RDF
[HPL-2003-142]
Jeremy J. Carroll's paper on signing RDF graphs.
Issue 19: Add history of the development of the c14n work

It's important for both patent and person credit reasons to include the full history.

Manu has offered to do this.

1.2 How to Read this Document

This document is a detailed specification for an RDF dataset canonicalization algorithm. The document is primarily intended for the following audiences:

To understand the basics in this specification you must be familiar with basic RDF concepts [RDF11-CONCEPTS]. A working knowledge of graph theory and graph isomorphism is also recommended.

1.3 Typographical conventions

This section is non-normative.

The following typographic conventions are used in this specification:

markup
Markup (elements, attributes, properties), machine processable values (string, characters, media types), property names, and file names are in red-orange monospace font.
variable
A variable in pseudo-code or in an algorithm description is italicized.
definition
A definition of a term, to be used elsewhere in this or other specifications, is italicized and in bold.
definition reference
A reference to a definition in this document is underlined and is also an active link to the definition itself.
markup definition reference
References to a definition in this document, when the reference itself is also a markup, is underlined, in a red-orange monospace font, and is also an active link to the definition itself.
external definition reference
A reference to a definition in another document is underlined and italicized, and is also an active link to the definition itself.
markup external definition reference
A reference to a definition in another document, when the reference itself is also a markup, is underlined and italicized in a red-orange monospace font, and is also an active link to the definition itself.
hyperlink
A hyperlink is underlined and in blue.
[reference]
A document reference (normative or informative) is enclosed in square brackets and links to the references section.
Explanation
An expandable area to find a more detailed, non-normative explanation of a particular algorithmic step.
Explanation

This area would provide more information about the step involved.

Logging
An expandable area to find suggestions for implementations to log information about processing, which may be useful in comparing with other implementations, or with logs provided with each test case.
Logging

For example, the following output snippet might describe the operation of an implementation using the [YAML] format.

Note

Notes are in light green boxes with a green left border and with a "Note" header in green. Notes are always informative.

Examples are in light khaki boxes, with khaki left border,
and with a numbered "Example" header in khaki.
Examples are always informative. The content of the example is in monospace font and may be 
syntax colored.

Examples may have tabbed navigation buttons
to show the results of transforming an example into other representations.

Code examples are generally given in a Turtle or TriG format for brevity,
where each line represents a single triple or quad.
Additionally, have the following implied directives:

BASE <http://example.com/>
PREFIX : <#>

Following the Turtle/TriG syntax rules, blank nodes always appear in the 
`_:xyz` format.

2. Conformance

As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.

The key words MUST and MUST NOT in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

3. Terminology

3.1 Terms defined by this specification

canonical n-quads form
The canonicalized representation of a quad is defined in A. A Canonical form of N-Quads. A quad in canonical n-quads form represents a graph name, if present, in the same manner as a subject, and each quad is terminated with a single new line character (U+000A).
gossip path
The gossip path between two blank nodes contained within quads in a dataset, where the path is a sequence of nodes and quads such that the first quad includes the starting node as an component, and the last quad includes the ending node as an component with each quad in the path containing both the preceding and following nodes.
hash
The lowercase, hexadecimal representation of a message digest.
hash algorithm
The hash algorithm used by URDNA2015, namely, SHA-256.
identifier issuer
An identifier issuer is used to issue new blank node identifiers. It maintains a blank node identifier issuer state.
input blank node identifier map
Records any blank node identifiers already assigned to the input dataset. If the input dataset is provided as an N-Quads document, the map relates blank nodes in the abstract input dataset to the blank node identifiers used within the N-Quads document, otherwise, identifiers are assigned arbitrarily for each blank node in the input dataset not previously identified.
Note
Implementations or environments might deal with blank node identifiers more directly; for example, some implementations might retain blank node identifiers in the parsed or abstract dataset. Implementations are expected to reuse these to enable usable mappings between input blank node identifiers and output blank node identifiers outside of the algorithm.
input dataset
The abstract RDF dataset that is provided as input to the algorithm.
mention
A node is mentioned in a quad if it is a component of that quad, as a subject, predicate, object, or graph name.
mention set
The set of all quads in a dataset that mention a node n is called the mention set of n, denoted Qn.
normalized dataset
A normalized dataset is the combination of the following: A concrete serialization of a normalized dataset MUST label all blank nodes using the canonical blank node identifiers.
quad
A tuple composed of subject, predicate, object, and graph name. This is a generalization of an RDF triple along with a graph name.

3.2 Terms defined by cited specifications

blank node
A blank node as specified by [RDF11-CONCEPTS]. In short, it is a node in a graph that is neither an IRI, nor a literal.
blank node identifier
A blank node identifier as specified by [RDF11-CONCEPTS]. In short, it is a string that begins with _: that is used as an identifier for a blank node. Blank node identifiers are typically implementation-specific local identifiers; this document specifies an algorithm for deterministically specifying them.
Concrete syntaxes, like [Turtle] or [N-Quads], prepend blank node identifiers with the _: string to differentiate them from other nodes in the graph. This affects the canonicalization algorithm, which is based on calculating a hash over the representations of quads in this format.
default graph
The default graph as specified by [RDF11-CONCEPTS].
graph name
A graph name as specified by [RDF11-CONCEPTS].
IRI
An IRI (Internationalized Resource Identifier) is a string that conforms to the syntax defined in [RFC3987].
object
An object as specified by [RDF11-CONCEPTS].
predicate
A predicate as specified by [RDF11-CONCEPTS].
RDF dataset
A dataset as specified by [RDF11-CONCEPTS]. For the purposes of this specification, an RDF dataset is considered to be a set of quads
RDF graph
An RDF graph as specified by [RDF11-CONCEPTS].
RDF triple
A triple as specified by [RDF11-CONCEPTS].
string
A string is a sequence of zero or more Unicode characters.
subject
A subject as specified by [RDF11-CONCEPTS].
true and false
Values that are used to express one of two possible boolean states.
Unicode code point order
This refers to determining the order of two Unicode strings (A and B), using Unicode Codepoint Collation, as defined in [XPATH-FUNCTIONS], which defines a total ordering of strings comparing code points.

4. Canonicalization

Canonicalization is the process of transforming an input dataset to its serialized canonical form. That is, any two input datasets that contain the same information, regardless of their arrangement, will be transformed into the same serialized canonical form. The problem requires directed graphs to be deterministically ordered into sets of nodes and edges. This is easy to do when all of the nodes have globally-unique identifiers, but can be difficult to do when some of the nodes do not. Any nodes without globally-unique identifiers must be issued deterministic identifiers.

Note

This specification defines a normalized dataset to include stable identifiers for blank nodes, practical uses of which will always generate a canonical serialization of such a dataset.

In time, there may be more than one canonicalization algorithm and, therefore, for identification purposes, this algorithm is named the "Universal RDF Dataset Canonicalization Algorithm 2015" (URDNA2015).

Editor's note

This statement is overly prescriptive and does not include normative language. This spec should describe the theoretical basis for graph canonicalization and describe behavior using normative statements. The explicit algorithms should follow as an informative appendix.

4.1 Overview

This section is non-normative.

To determine a canonical labeling, URDNA2015 considers the information connected to each blank node. Nodes with unique first degree information can immediately be issued a canonical identifier via the Issue Identifier algorithm. When a node has non-unique first degree information, it is necessary to determine all information that is transitively connected to it throughout the entire dataset. 4.6 Hash First Degree Quads defines a node’s first degree information via its first degree hash.

Hashes are computed from the information of each blank node. These hashes encode the mentions incident to each blank node. The hash of a string s, is the lower-case, hexadecimal representation of the result of passing s through a cryptographic hash function. URDNA2015 uses the SHA-256 hash algorithm.

4.2 Canonicalization State

When performing the steps required by the canonicalization algorithm, it is helpful to track state in a data structure called the canonicalization state. The information contained in the canonicalization state is described below.

blank node to quads map
A map that relates a blank node identifier to the quads in which they appear in the input dataset.
hash to blank nodes map
A map that relates a hash to a list of blank node identifiers.
canonical issuer
An identifier issuer, initialized with the prefix c14n, for issuing canonical blank node identifiers.
Editor's note
Mapping all blank nodes to use this identifier spec means that an RDF dataset composed of two different RDF graphs will issue different identifiers than that for the graphs taken independently. This may happen anyway, due to automorphisms, or overlapping statements, but an identifier based on the resulting hash along with an issue sequence number specific to that hash would stand a better chance of surviving such minor changes, and allow the resulting information to be useful for RDF Diff.

4.3 Blank Node Identifier Issuer State

The canonicalization algorithm issues identifiers to blank nodes. The Issue Identifier algorithm uses an identifier issuer to accomplish this task. The information an identifier issuer needs to keep track of is described below.

identifier prefix
The identifier prefix is a string that is used at the beginning of an blank node identifier. It should be initialized to a string that is specified by the canonicalization algorithm. When generating a new blank node identifier, the prefix is concatenated with a identifier counter. For example, c14n is a proper initial value for the identifier prefix that would produce blank node identifiers like c14n1.
identifier counter
A counter that is appended to the identifier prefix to create an blank node identifier. It is initialized to 0.
issued identifiers map
An ordered map that relates blank node identifiers to issued identifiers, to prevent issuance of more than one new identifier per existing identifier, and to allow blank nodes to be assigned identifiers some time after issuance.

4.4 Canonicalization Algorithm

Editor's note

At the time of writing, there are several open issues that will determine important details of the canonicalization algorithm.

Issue 7: Support generalized RDF propose closing

Generalized RDF is described in RDF 1.1 Concepts and Abstract Syntax.

It removes restrictions on the type of RDF term that can occur in any slot in a quad tuple - literals as subjects or predicates, blank nodes as predicates etc. By implication, that would include RDF-start quoted triples.

RDF 1.1 separately changed "RDF dataset" to allow blank nodes for in the graph slot.

Generalized RDF does arise - for example, in some rules systems.

Covering generalized RDF gives some future proofing.

Issue 8: "Herd-privacy" canonicalization propose closing

I completely agree with the importance of the "herd-privacy" canonicalization proposed in #4 (comment) by @dlongley when we use c14n with selective disclosure. However, if I understand it correctly, we would still have to improve the above algorithm; it seems to me that the following normalized datasets CX1 and CX2 are not modified via the above transformation, i.e., CX1==CY1 and CX2==CY2.

CX1 (obtained from JSON-LD Playground) (==CY1)

_:c14n0 <http://schema.org/name> "Alice" .
_:c14n0 <http://schema.org/spouse> _:c14n1 .
_:c14n1 <http://schema.org/name> "Bob" .

CX2 (obtained from JSON-LD Playground) (==CY2)

_:c14n0 <http://schema.org/name> "Carl" .
_:c14n1 <http://schema.org/name> "Alice" .
_:c14n1 <http://schema.org/spouse> _:c14n0 .

Therefore, even if Alice selectively hides the statement about her spouse, anyone can easily guess whether Bob or Carl is Alice's spouse based on the canonicalized identifiers or the order of unrevealed statement:

CY1 with selective disclosure

_:c14n0 <http://schema.org/name> "Alice" .
_:c14n0 <http://schema.org/spouse> _:c14n1 .
### 3rd statement is unrevealed ####

CY2 with selective disclosure

### 1st statement is unrevealed ####
_:c14n1 <http://schema.org/name> "Alice" .
_:c14n1 <http://schema.org/spouse> _:c14n0 .

What we actually wanted seemed like the following result:

CY1'

_:c14n0 <http://schema.org/name> "Alice" .
_:c14n1 <http://schema.org/name> "Bob" .
_:c14n0 <http://schema.org/spouse> _:c14n1 .

CY2'

_:c14n0 <http://schema.org/name> "Alice" .
_:c14n1 <http://schema.org/name> "Carl" .
_:c14n0 <http://schema.org/spouse> _:c14n1 .

I am trying to figure out a solution, but haven't found one yet so just submitting this issue at the moment...

Issue 10: C14N choice criteria documentation

In the meeting on 2022-10-12, we discussed criteria that can be used to make a choice between alternative choices in specific steps in the c14n algorithm. The initial list of suggestions is below. We need to formalize this and, IMO, include it in the explainer doc.

  • ease of implementation
  • existing incubation / use in the marketplace
  • time / resource complexity in solving common datasets
  • time / resource complexity in solving complex (or poison?) datasets
  • Existence of formal proofs for the algorithms
  • Demonstration of review of formal proofs for the algorithms
  • reusing existing primitives that are available on various platforms
  • cover real life examples
Issue 11: Recording the canonicalization and hashing applied. propose closing

In the meeting on 2022-10-12, there was mention of needing to say which choices were made in generating the hash.

We already have the existing URDNA2015 as the canonicalization algorithm. Anything this working group does that makes any change to that will need to have a way to declare which algorithm was used to produce the resultant canonical form.

There may be good reasons for different hashing and signing algorithms.

We need a naming scheme of choices made, together with a way to transmit that information.

See #4 about the output of canonicalization.

Issue 16: Optionally fail on duplicates in Hash N-Degree Quads? question

From the CG spec:

An additional input to this algorithm should be added that
allows it to be optionally skipped and throw an error if any
equivalent related hashes were produced that must be permuted
during step 5.4.4. For practical uses of the algorithm, this step
should never be encountered and could be turned off, disabling
canonizing datasets that include a need to run it as a security
measure.

Issue 84: Some privacy considerations ms:CRready for prHorizontalReview

As a starting point for preparing Privacy Considerations (#70), I am trying to organize some existing discussions about privacy aspects of the URDNA2015, including:

While the discussions here mainly focus on URDNA2015, I believe we can apply similar arguments to the other RDF canonicalization algorithms.


Privacy considerations here are primarily worth discussing when the canonicalization scheme is used for privacy-respecting signed RDF dataset.

The following issues are worth discussing when the canonicalization scheme is used for privacy-respecting signed RDF datasets and are likely acceptable for other use cases. One of the former examples is a verifiable credential with selective disclosure.

Selective disclosure is the ability for someone to share only some of the statements from a signed dataset, without harming the ability of the recipient to verify the authenticity of those selected statements. (Note: copied from Dave's comment in #4)

The normalized dataset, the output of the canonicalization algorithm described in this specification, may leak partial information about undisclosed statements and help the adversary correlate the original and disclosed datasets.

a) possible leakage via canonical labeling

If a dataset contains at least two blank nodes, the canonical labeling can be exploited to guess the undisclosed quad in the dataset.

For example, let us assume we have the following dataset to be signed. (Note: this person is fictitious, prepared only to make this example work.)

# original dataset
_:b0 <http://schema.org/address> _:b1 .
_:b0 <http://schema.org/familyName> "Jarrett" .
_:b0 <http://schema.org/gender> "Female" .  # gender === Female
_:b0 <http://schema.org/givenName> "Ali" .
_:b0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Person> .
_:b1 <http://schema.org/addressCountry> "United States" .
_:b1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/PostalAddress> .

Using URDNA2015, we can obtain the normalized dataset with canonical labels sorted in the canonical (code-point) order.

# normalized dataset
_:c14n0 <http://schema.org/addressCountry> "United States" .
_:c14n0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/PostalAddress> .
_:c14n1 <http://schema.org/address> _:c14n0 .
_:c14n1 <http://schema.org/familyName> "Jarrett" .
_:c14n1 <http://schema.org/gender> "Female" .  # gender === Female
_:c14n1 <http://schema.org/givenName> "Ali" .
_:c14n1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Person> .

The signer (or issuer) can generate a signature for the dataset by first hashing each statement and then signing them using a multi-message digital signature scheme like BBS+. The resulting dataset with signature is passed to the holder, who can control whether or not to disclose each statement while maintaining their verifiability.

Let us say that the holder wants to show her attributes except for gender to a verifier. Then the holder should disclose the following partial dataset. (Note: proofs omitted here for brevity)

# disclosed dataset
_:c14n0 <http://schema.org/addressCountry> "United States" .
_:c14n0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/PostalAddress> .
_:c14n1 <http://schema.org/address> _:c14n0 .
_:c14n1 <http://schema.org/familyName> "Jarrett" .
### 5th statement is unrevealed ##
_:c14n1 <http://schema.org/givenName> "Ali" .
_:c14n1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Person> .

However, in this example, anyone can guess the unrevealed statement by exploiting the canonical labels and order.

Since the dataset was sorted in the canonical order, we can get to know that the hidden statement must start with _:c14n1 <http://schema.org/[f-g], which helps us guess that the hidden predicate is <http://schema.org/gender> with high probability.
Alternatively, we can assume that the guesser already has such knowledge via the public credential schema.

Then, if the canonical labeling produces different results depending on the gender value, we can use it to deduce the gender value.
In fact, this example produces different results depending on whether the gender is Female or Male.
(Note: I ignored the other types of gender just for brevity)

The following example shows that "gender = Male" yields different canonical labeling.

# hypothetical normalized dataset
_:c14n0 <http://schema.org/address> _:c14n1 .
_:c14n0 <http://schema.org/familyName> "Jarrett" .
_:c14n0 <http://schema.org/gender> "Male" .  # gender === Male
_:c14n0 <http://schema.org/givenName> "Ali" .
_:c14n0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Person> .
_:c14n1 <http://schema.org/addressCountry> "United States" .
_:c14n1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/PostalAddress> .

So the verifier should have obtained the following dataset if gender had the value Male, which differs from the revealed dataset, so the verifier can conclude that the gender is Female.

# hypothetical disclosed dataset
_:c14n0 <http://schema.org/address> _:c14n1 .
_:c14n0 <http://schema.org/familyName> "Jarrett" .
### 3rd statement is unrevealed ##
_:c14n0 <http://schema.org/givenName> "Ali" .
_:c14n0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Person> .
_:c14n1 <http://schema.org/addressCountry> "United States" .
_:c14n1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/PostalAddress> .

Note that we can use the same approach to guess non-boolean values if the range of possible values is still a reasonable (small) size for a guesser to try all the possibilities.

By making the canonicalization process private, we can prevent a brute-forcing attacker from trying to see the labeling change by trying multiple possible attribute values.
For example, we can use a HMAC instead of a hash function in the canonicalization algorithm. Alternatively, we can add a secret random nonce (always undisclosed) into the dataset.
Note that these workarounds force dataset issuers and holders to manage shared secrets securely.
We also note that these workarounds adversely affect the unlinkability described below because canonical labeling now varies depending on the secret shared by the issuer and the holder, which will help correlate them.

b) possible leakage via canonical sorting

The canonical order can leak unrevealed information even without canonical labelings.

Let us assume that the holder has the following signed dataset, sorted in the canonical (code-point) order.

:a <http://schema.org/children> "Albert" .
:a <http://schema.org/children> "Alice" .
:a <http://schema.org/children> "Allie" .
:a <http://schema.org/name> "John Smith" .

If the holder wants to hide the statement for their second child for any reason, the disclosed dataset now looks like this:

:a <http://schema.org/children> "Albert" .
### 2nd statement is unrevealed ##
:a <http://schema.org/children> "Allie" .
:a <http://schema.org/name> "John Smith" .

Knowing that these statements are sorted in the canonical order, we can guess that the hidden statement must start with :a <http://schema.org/children> "Al, which leaks the subject (:a), predicate (<http://schema.org/children>) and the first two letters of the object ("Al") in the hidden statement.

To avoid this leakage, the dataset issuer can randomly shuffle the normalized statements before signing and issuing them to the holder, preventing others from guessing undisclosed information from the canonical order.
However, similar to the workarounds mentioned above, this workaround also adversely affects unlinkability. This is because there are $n!$ different permutations for shuffling $n$ statements, and whichever one is used will help correlate the dataset.

c) possible linking via canonical labeling

Unlinkability ensures that no correlatable data are used in a signed dataset while still providing some level of trust, the sufficiency of which must be determined by each verifier. (Note: based on the description in the VC Data Integrity spec)

While canonical sorting works better for unlinkability, canonical labeling can be exploited to break it.
The total number of canonical labelings for a dataset with $n$ blank nodes is $n!$, which is not controllable by the issuer.
It means that the herd constructed as a result of selective disclosure will be split into $n!$ pieces due to the canonical labeling, which reduces unlinkability.

For example, let us assume that an employee of the small company "example.com" shows its employee ID dataset without their name like this:

# disclosed dataset
### 1st statement is unrevealed ##
_:c14n0 <http://schema.org/worksFor> _:c14n1 .
_:c14n0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Person> .
_:c14n1 <http://schema.org/address> _:c14n2 .
_:c14n1 <http://schema.org/geo> _:c14n3 .
_:c14n1 <http://schema.org/name> "example.com" .
_:c14n2 <http://schema.org/addressCountry> "United States" .
_:c14n3 <http://schema.org/latitude> "0.0" .
_:c14n3 <http://schema.org/longitude> "0.0" .

The verifier can always trace this person without knowing their name if this company has only three employees with the following employee ID datasets.

# normalized dataset 1
_:c14n0 <http://schema.org/address> _:c14n1 .
_:c14n0 <http://schema.org/geo> _:c14n3 .
_:c14n0 <http://schema.org/name> "example.com" .
_:c14n1 <http://schema.org/addressCountry> "United States" .
_:c14n2 <http://schema.org/name> "Jayden Doe" .
_:c14n2 <http://schema.org/worksFor> _:c14n0 .
_:c14n2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Person> .
_:c14n3 <http://schema.org/latitude> "0.0" .
_:c14n3 <http://schema.org/longitude> "0.0" .
# normalized dataset 2
_:c14n0 <http://schema.org/address> _:c14n1 .
_:c14n0 <http://schema.org/geo> _:c14n2 .
_:c14n0 <http://schema.org/name> "example.com" .
_:c14n1 <http://schema.org/addressCountry> "United States" .
_:c14n2 <http://schema.org/latitude> "0.0" .
_:c14n2 <http://schema.org/longitude> "0.0" .
_:c14n3 <http://schema.org/name> "Morgan Doe" .
_:c14n3 <http://schema.org/worksFor> _:c14n0 .
_:c14n3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Person> .
# normalized dataset 3
_:c14n0 <http://schema.org/name> "Johnny Smith" .
_:c14n0 <http://schema.org/worksFor> _:c14n1 .
_:c14n0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://schema.org/Person> .
_:c14n1 <http://schema.org/address> _:c14n2 .
_:c14n1 <http://schema.org/geo> _:c14n3 .
_:c14n1 <http://schema.org/name> "example.com" .
_:c14n2 <http://schema.org/addressCountry> "United States" .
_:c14n3 <http://schema.org/latitude> "0.0" .
_:c14n3 <http://schema.org/longitude> "0.0" .

The canonicalization in this example produces different labelings for these three employees, which helps anyone to correlate their activities even if they do not reveal their names in the dataset.

By determining some "template" for each anonymous set (or herd) and fixing the canonical labeling and canonical order used in the anonymous set, we can achieve a certain unlinkability.
Alternatively, we might be able to generate a kind of "on-the-fly" template proposed in Dave's comment in #4, which seems ineffective in some cases (see #8).

Issue 87: Naming the algorithm (Bike-shedding danger ahead!)

I must admit I hate the name "URDNA2015". I never remember what URDNA stands for (I always associate it with some weird gene...) and the 2015 is irrelevant.

Shouldn't we come with a simpler name? We could simply call it RCH, for example and, if we need it, we can use the "usual" W3C versioning habit of calling it RCH 1.0.

Just a thought. But it is still time to make this change.

Issue 88: Should multiformats refer to URDNA or URDCA?

there is a PR that contributed a reference to some of this in multiformats/multicodec

the pr title says 'urdna' but the pr added the name using 'urdca'. Which is more... canonical.... normal.... whatever. What should we put it in the multicodec name column? :)

I imagine this decision has editorial implications for the spec itself? It still says 'urdna2015' quite a bit and does not contain 'urdca'. So why should multibase say 'urdca'?

Relates to:

Issue 89: Support ordered input dataset or "list of quads" and optional mapping from input indices to output indices enhancementspec:enhancementready for pr

Starting with my comment here: #86 (comment)

Some discussion spawned around the need, as an optional output, a mapping of quad input indices to quad output indices for selective disclosure use cases.

@gkellogg made this comment:

Just be be clear on what we're talking about, the input dataset is unordered and no blank node labels are persistent or possibly even present.

Which implies that we might want to also take an ordered list of quads as an optional alternative input to the algorithm. Or perhaps we can describe the RDF abstract dataset as being optionally represented as such -- for the case where this mapping output is desirable. Notably, the presence (or lack thereof) of input blank node labels in this case is not relevant.

The canonicalization algorithm converts an input dataset into a normalized dataset. This algorithm will assign deterministic identifiers to any blank nodes in the input dataset.

4.4.1 Overview

This section is non-normative.

URDNA2015 canonically labels an RDF dataset by assigning each blank node a canonical identifier. In URDNA2015, an RDF dataset D is represented as a set of quads of the form < s, p, o, g > where the graph component g is empty if and only if the triple < s, p, o > is in the default graph. It is expected that, for two RDF datasets, URDNA2015 returns the same canonically labeled list of quads if and only if the two datasets are isomorphic (i.e., the same modulo blank node identifiers).

URDNA2015 consists of several sub-algorithms. These sub-algorithms are introduced in the following sub-sections. First, we give a high level summary of URDNA2015.

  1. Initialization. Initialize the state needed for the rest of the algorithm using 4.2 Canonicalization State. Also initialize the normalized dataset using the input dataset (which remains immutable) the input blank node identifier map (retaining blank node identifiers from the input if possible, otherwise assigning them arbitrarily); the canonical issuer is added upon completion of the algorithm.
  2. Compute first degree hashes. Compute the first degree hash for each blank node in the dataset using 4.6 Hash First Degree Quads.
  3. Canonically label unique nodes. Assign canonical identifiers via 4.5 Issue Identifier Algorithm, in Unicode code point order, to each blank node whose first degree hash is unique.
  4. Compute N-degree hashes for non-unique nodes. For each repeated first degree hash (proceeding in Unicode code point order), compute the N-degree hash via 4.8 Hash N-Degree Quads of every unlabeled blank node that corresponds to the given repeated hash.
  5. Canonically label remaining nodes. In Unicode code point order of the N-degree hashes, issue canonical identifiers to each corresponding blank node using 4.5 Issue Identifier Algorithm. If more than one node produces the same N-degree hash, the order in which these nodes receive a canonical identifier does not matter.
  6. Finish. Return the serialized canonical form of the normalized dataset. Alternatively, return the normalized dataset containing the input blank node identifier map and canonical issuer.

4.4.2 Examples

This section is non-normative.

4.4.3 Algorithm

  1. Create the canonicalization state. If the input dataset is an N-Quads document, parse that document into a dataset in the normalized dataset, retaining any blank node identifiers used within that document in the input blank node identifier map; otherwise arbitrary identifiers are assigned for each blank node.
    Explanation

    This has the effect of initializing the blank node to quads map, and the hash to blank nodes map, as well as instantiating a new canonical issuer.

    After this algorithm completes, the input blank node identifier map state and canonical issuer may be used to correlate blank nodes used in the input dataset with both their original identifiers, and associated canonical identifiers.

  2. For every quad Q in input dataset:
    1. For each blank node that is a component of Q, add a reference to Q from the map entry for the blank node identifier identifier in the blank node to quads map, creating a new entry if necessary, using the identifier for the blank node found in the input blank node identifier map.
      Explanation

      This establishes the blank node to quads map, relating each blank node with the set of quads of which it is a component, via the map for each blank node in the input dataset to its assigned identifier.

      Note

      Literal components of quads are not subject to any normalization. As noted in Section 3.3 of [RDF11-CONCEPTS], literal term equality is based on the lexical form, rather than the literal value, so two literals "01"^^xs:integer and "1"^^xs:integer are treated as distinct resources.

    Logging

    Log the state of the blank node to quads map:

  3. For each key n in the blank node to quads map:
    Explanation

    This step creates a hash for every blank node in the input document. Some blank nodes will lead to a unique hash, while other blank nodes may share a common hash.

    1. Create a hash, hf(n), for n according to the Hash First Degree Quads algorithm.
    2. Add hf(n) and n to hash to blank nodes map, including repetitions, creating a new entry if necessary.
    Logging

    Log the results from the Hash First Degree Quads algorithm.

  4. For each hash to identifier list map entry in hash to blank nodes map, code point ordered by hash:
    Explanation

    This step establishes the canonical identifier for blank nodes having a unique hash, which are recorded in the canonical issuer.

    1. If identifier list has more than one entry, continue to the next mapping.
    2. Use the Issue Identifier algorithm, passing canonical issuer and the single blank node identifier, identifier in identifier list to issue a canonical replacement identifier for identifier.
    3. Remove the map entry for hash from the hash to blank nodes map.
    Logging

    Log the assigned canonical identifiers.

  5. For each hash to identifier list map entry in hash to blank nodes map, code point ordered by hash:
    Explanation

    This step establishes the canonical identifier for blank nodes having a shared hash. This is done by creating unique blank node identifiers for all blank nodes traversed by the Hash N-Degree Quads algorithm, running through each blank node without a canonical identifier in the order of the hashes established in the previous step.

    Logging

    Log hash and identifier list for this iteration.

    1. Create hash path list where each item will be a result of running the Hash N-Degree Quads algorithm.
      Explanation

      This list establishes an order for those blank nodes sharing a common first-degree hash.

    2. For each blank node identifier n in identifier list:
      1. If a canonical identifier has already been issued for n, continue to the next blank node identifier.
      2. Create temporary issuer, an identifier issuer initialized with the prefix b.
      3. Use the Issue Identifier algorithm, passing temporary issuer and n, to issue a new temporary blank node identifier bn to n.
      4. Run the Hash N-Degree Quads algorithm, passing the canonicalization state, n for identifier, and temporary issuer, appending the result to the hash path list.
        Logging

        Include logs for each call to Hash N-Degree Quads algorithm.

    3. For each result in the hash path list, code point ordered by the hash in result:
      Explanation

      The previous step created temporary identifiers for the blank nodes sharing a common first degree hash, which is now used to generate their canonical identifiers.

      1. For each blank node identifier, existing identifier, that was issued a temporary identifier by identifier issuer in result, issue a canonical identifier, in the same order, using the Issue Identifier algorithm, passing canonical issuer and existing identifier.
        Explanation

        In Step 5.2, hash path list was created with an ordered set of results. Each result contained a temporary issuer which recorded temporary identifiers associated with a particular blank node identifier in identifier list. This step processes each returned temporary issuer, in order, and allocates canonical identifiers to the temporary identifier mappings contained within each temporary issuer, creating a full order on the remaining blank nodes with unissued canonical identifiers.

      Logging

      Log newly issued canonical identifiers.

  6. Add the canonical issuer to the normalized dataset.
    Explanation

    This step adds the canonical issuer to the normalized dataset, the keys in the canonical issuer with the map entries of the input blank node identifier map.

    Logging

    Log the state of the canonical issuer at the completion of the algorithm.

  7. Return the serialized canonical form of the normalized dataset. Upon request, alternatively (or additionally) return the normalized dataset itself, which includes the input blank node identifier map, and canonical issuer.
    Note

    Technically speaking, one implementation might return a normalized dataset that maps particular blank nodes to different identifiers than another implementation, however, this only occurs when there are isomorphisms in the dataset such that a canonically serialized expression of the dataset would appear the same from either implementation.

    Explanation

    The serialized canonical form is an N-Quads document where the blank node identifiers are taken from the canonical identifiers associated with each blank node.

    The normalized dataset is composed of the original input dataset, the input blank node identifier map, containing identifiers for each blank node in the input dataset, and the canonical issuer, containing an issued identifiers map mapping the identifiers in the input blank node identifier map to their canonical identifiers.

4.5 Issue Identifier Algorithm

This algorithm issues a new blank node identifier for a given existing blank node identifier. It also updates state information that tracks the order in which new blank node identifiers were issued. The order of issuance is important for canonically labeling blank nodes that are isomorphic to others in the dataset.

4.5.1 Overview

The algorithm maintains an issued identifiers map to relate an existing blank node identifier from the input dataset to a new blank node identifier using a given identifier prefix (c14n) with new identifiers issued by appending an incrementing number. For example, when called for a blank node identifier such as e3, it might result in a issued identifier of c14n1.

4.5.2 Algorithm

The algorithm takes an identifier issuer I and an existing identifier as inputs. The output is a new issued identifier. The steps of the algorithm are:

  1. If there is a map entry for existing identifier in issued identifiers map of I, return it.
  2. Generate issued identifier by concatenating identifier prefix with the string value of identifier counter.
  3. Add an entry mapping existing identifier to issued identifier to the issued identifiers map of I.
  4. Increment identifier counter.
  5. Return issued identifier.

4.6 Hash First Degree Quads

This algorithm calculates a hash for a given blank node across the quads in a dataset in which that blank node is a component. If the hash uniquely identifies that blank node, no further examination is necessary. Otherwise, a hash will be created for the blank node using the algorithm in 4.8 Hash N-Degree Quads invoked via 4.4 Canonicalization Algorithm.

4.6.1 Overview

This section is non-normative.

To determine whether the first degree information of a node n is unique, a hash is assigned to its mention set, Qn. The first degree hash of a blank node n, denoted hf(n), is the hash that results from 4.6 Hash First Degree Quads when passing n. Nodes with unique first degree hashes have unique first degree information.

For consistency, blank node identifiers used in Qn are replaced with placeholders in a canonical n-quads serialization of that quad. Every blank node component is replaced with either a or z, depending on if that component is n or not.

The resulting serialized quads are then code point ordered, concatenated, and hashed. This hash is the first degree hash of n, hf(n).

4.6.2 Examples

This section is non-normative.

4.6.3 Algorithm

This algorithm takes the canonicalization state and a reference blank node identifier as inputs.

  1. Initialize nquads to an empty list. It will be used to store quads in canonical n-quads form.
  2. Get the list of quads quads from the map entry for reference blank node identifier in the blank node to quads map.
  3. For each quad quad in quads:
    1. Serialize the quad in canonical n-quads form with the following special rule:
      1. If any component in quad is an blank node, then serialize it using a special identifier as follows:
        1. If the blank node's existing blank node identifier matches the reference blank node identifier then use the blank node identifier a, otherwise, use the blank node identifier z.
  4. Sort nquads in Unicode code point order.
  5. Return the hash that results from passing the sorted and concatenated nquads through the hash algorithm.
    Logging

    Log the inputs and result of running this algorithm.

4.8 Hash N-Degree Quads

This algorithm calculates a hash for a given blank node across the quads in a dataset in which that blank node is a component for which the hash does not uniquely identify that blank node. This is done by expanding the search from quads directly referencing that blank node (the mention set), to those quads which contain nodes which are also components of quads in the mention set, called the gossip path. This process proceeds in every greater degrees of indirection until a unique hash is obtained.

Editor's note

The 'path' terminology could also be changed to better indicate what a path is (a particular deterministic serialization for a subgraph/subdataset of nodes without globally-unique identifiers).

4.8.1 Overview

This section is non-normative.

Usually, when trying to determine if two nodes in a graph are equivalent, you simply compare their identifiers. However, what if the nodes don't have identifiers? Then you must determine if the two nodes have equivalent connections to equivalent nodes all throughout the whole graph. This is called the graph isomorphism problem. This algorithm approaches this problem by considering how one might draw a graph on paper. You can test to see if two nodes are equivalent by drawing the graph twice. The first time you draw the graph the first node is drawn in the center of the page. If you can draw the graph a second time such that it looks just like the first, except the second node is in the center of the page, then the nodes are equivalent. This algorithm essentially defines a deterministic way to draw a graph where, if you begin with a particular node, the graph will always be drawn the same way. If two graphs are drawn the same way with two different nodes, then the nodes are equivalent. A hash is used to indicate a particular way that the graph has been drawn and can be used to compare nodes.

When two blank nodes have the same first degree hash, extra steps must be taken to detect global, or N-degree, distinctions. All information that is in any way connected to the blank node n through other blank nodes, even transitively, must be considered.

To consider all transitive information, the algorithm traverses and encodes all possible paths of incident mentions emanating from n, called gossip paths, that reach every unlabeled blank node connected to n. Each unlabeled blank node is assigned a temporary identifier in the order in which it is reached in the gossip path being explored. The mentions that are traversed to reach connected blank nodes are encoded in these paths via related hashes. This provides a deterministic way to order all paths coming from n that reach all blank nodes connected to n without relying on input blank node identifiers.

This algorithm works in concert with the main canonicalization algorithm to produce a unique, deterministic identifier for a particular blank node. This hash incorporates all of the information that is connected to the blank node as well as how it is connected. It does this by creating deterministic paths that emanate out from the blank node through any other adjacent blank nodes.

Ultimately, the algorithm selects a shortest gossip path, distributing canonical identifiers to the unlabeled blank nodes in the order in which they appear in this path. The hash of this encoded shortest path, called the N-degree hash of n, distinguishes n from other blank nodes in the dataset.

For clarity, we consider a gossip path encoded via the string s to be shortest provided that:

  1. The length of s is less than or equal to the length of any other gossip path string s′.
  2. If s and s′ have the same length (as strings), then s is code point ordered less than or equal to s′.

For example, abc is shorter than bbc, whereas abcd is longer than bcd.

The following provides a high level outline for how the N-degree hash of n is computed along the shortest gossip path. Note that the full algorithm considers all gossip paths, ultimately returning the hash of the shortest encoded path.

  1. Compute related hashes. Compute the related hash Hn set for n, i.e., all first degree mentions between n and another blank node. Note that this includes both unlabeled blank nodes and those already issued a canonical identifier (labeled blank nodes).
  2. Explore mentions. Given the related hash x in Hn, record x in the data to hash Dn. Determine whether each blank node reachable via the mention with related hash x has already received an identifier.
    1. Record the identifiers of labeled nodes. If a blank node already has an identifier, record its identifier in Dn once for every mention with related hash x. Skip to the next related hash in Hn and repeat step 2.
    2. Distribute and record temporary identifiers to unlabeled nodes. For each unlabeled blank node, assign it a temporary identifier according to the order in which it is reached in the gossip path, recording its given identifier in Dn (including repetitions). Add each unlabeled node to the recursion list Rn(x) in this same order (omitting repetitions).
    3. Recurse on newly labeled nodes. For each ni in Rn(x)
      1. Record its identifier in Dn
      2. Append < r(i) > to Dn where r(i) is the data to hash that results from returning to step 1, replacing n with ni.
  3. Compute the N-degree hash of n. Hash Dn to return the N-degree hash of n, namely hN(n). Return the updated issuer In that has now distributed temporary identifiers to all unlabeled blank nodes connected to n.

As described above in step 2.3, HN recurses on each unlabeled blank node when it is first reached along the gossip path being explored. This recursion can be visualized as moving along the path from n to the blank node ni that is receiving a temporary identifier. If, when recursing on ni, another unlabeled blank node nj is discovered, the algorithm again recurses. Such a recursion traces out the gossip path from n to nj via ni.

The recursive hash r(i) is the hash returned from the completed recursion on the node ni when computing hN(n). Just as hN(n) is the hash of Dn, we denote the data to hash in the recursion on ni as Di. So, r(i) = h(Di). For each related hash xHn, Rn(x) is called the recursion list on which the algorithm recurses.

4.8.2 Examples

This section is non-normative.

4.8.3 Algorithm

Issue 16: Optionally fail on duplicates in Hash N-Degree Quads? question
An additional input to this algorithm should be added that allows it to be optionally skipped and throw an error if any equivalent related hashes were produced that must be permuted during step 5.4.4. For practical uses of the algorithm, this step should never be encountered and could be turned off, disabling canonizing datasets that include a need to run it as a security measure.

The inputs to this algorithm are the canonicalization state, the identifier for the blank node to recursively hash quads for, and path identifier issuer which is an identifier issuer that issues temporary blank node identifiers. The output from this algorithm will be a hash and the identifier issuer used to help generate it.

Logging

Log the inputs to the algorithm.

  1. Create a new map Hn for relating hashes to related blank nodes.
  2. Get a reference, quads, to the list of quads from the map entry for identifier in the blank node to quads map.
    Explanation

    quads is the mention set of identifier.

    Logging

    Log the quads from the mention set of identifier.

  3. For each quad in quads:
    Explanation

    This loop calculates the related hash Hn for other blank nodes within the mention set of identifier.

    1. For each component in quad, where component is the subject, object, or graph name, and it is a blank node that is not identified by identifier:
      1. Set hash to the result of the Hash Related Blank Node algorithm, passing the blank node identifier for component as related, quad, issuer, and position as either s, o, or g based on whether component is a subject, object, graph name, respectively.
      2. Add a mapping of hash to the blank node identifier for component to Hn, adding an entry as necessary.
    Logging

    Include the logs for each iteration of the Hash Related Blank Node algorithm and the resulting Hn.

  4. Create an empty string, data to hash.
  5. For each related hash to blank node list mapping in Hn, code point ordered by related hash:
    Explanation

    This loop explores the gossip paths for each related blank node sharing a common hash to identifier finding the shortest such path (chosen path). This determines how canonical identifiers for otherwise commonly hashed blank nodes are chosen.

    Each path is represented by the concatenation of the identifiers for each related blank node – either the issued identifier, or a temporary identifier created using a copy of issuer. Those for which temporary identifiers were issued are later recursed over using this algorithm.

    Logging

    Log the value of related hash and state of data to hash.

    1. Append the related hash to the data to hash.
    2. Create a string chosen path.
    3. Create an unset chosen issuer variable.
    4. For each permutation p of blank node list:
      Logging

      Log each permutation p.

      1. Create a copy of issuer, issuer copy.
      2. Create a string path.
      3. Create a recursion list, to store blank node identifiers that must be recursively processed by this algorithm.
      4. For each related in p:
        1. If a canonical identifier has been issued for related by canonical issuer, append the string _:, followed by the canonical identifier for related, to path.
          Explanation

          A canonical identifier may have been generated before calling this algorithm, if it was issued from an earlier call to Hash First Degree Quads algorithm. There is no reason to recurse and apply the algorithm to any related blank node that has already been assigned a canonical identifier. Furthermore, using the canonical identifier also further distinguishes it from any temporary identifier, allowing for even greater efficiency in finding the chosen path.

        2. Otherwise:
          1. If issuer copy has not issued an identifier for related, append related to recursion list.
            Explanation

            Temporarily labeled nodes have identifiers recorded in issuer copy, which is later used to recursively call this algorithm, so that eventually all nodes are given canonical identifiers.

          2. Use the Issue Identifier algorithm, passing issuer copy and the related, and append the string _:, followed by the result, to path.
        3. If chosen path is not empty and the length of path is greater than or equal to the length of chosen path and path is greater than chosen path when considering code point order, then skip to the next permutation p.
          Explanation

          If path is already longer than the prospective chosen path, we can terminate this iteration early.

        Explanation

        path is used to generate a hash at a later step; in this respect, it is similar to the Hash First Degree Quads algorithm which uses the serialization of quads in nquads for hashing. For the sake of consistency, the nquad representation of blank node identifiers is used in these steps, hence the usage of the _: string.

        Logging

        Log related and path.

      5. For each related in recursion list:
        Explanation

        The prospective path is extended with the hash resulting from recursively calling this algorithm on each related blank node issued a temporary identifier.

        Logging

        Log recursion list and path.

        1. Set result to the result of recursively executing the Hash N-Degree Quads algorithm, passing the canonicalization state, related for identifier, and issuer copy for path identifier issuer.
          Logging

          Log related and include logs for each recursive call to Hash N-Degree Quads algorithm.

        2. Use the Issue Identifier algorithm, passing issuer copy and related; append the string _:, followed by the result, to path.
        3. Append <, the hash in result, and > to path.
        4. Set issuer copy to the identifier issuer in result.
        5. If chosen path is not empty and the length of path is greater than or equal to the length of chosen path and path is greater than chosen path when considering code point order, then skip to the next p.
          Explanation

          If path is already longer than the prospective chosen path, we can terminate this iteration early.

      6. If chosen path is empty or path is less than chosen path when considering code point order, set chosen path to path and chosen issuer to issuer copy.
    5. Append chosen path to data to hash.
      Logging

      Log chosen path and data to hash.

    6. Replace issuer, by reference, withchosen issuer.
  6. Return issuer and the hash that results from passing data to hash through the hash algorithm.
    Logging

    Log issuer and results from passing data to hash through the hash algorithm.

5. Serialization

This section describes the process of creating a serialized [N-Quads] representation of a normalized dataset.

The serialized canonical form of a normalized dataset is an N-Quads document [N-QUADS] created by representing each quad from the normalized dataset in canonical n-quads form, sorting them into code point order, and concatenating them. (Note that each canonical N-Quads statement ends with a new line, so no additional separators are needed in the concatenation.) The resulting document has a media type of application/n-quads, as described in C. N-Quads Internet Media Type, File Extension and Macintosh File Type of [N-QUADS].

When serializing quads in canonical n-quads form, components which are blank nodes MUST be serialized using the canonical label associated with each blank node from the map component of the normalized dataset.

6. Privacy Considerations

This section is non-normative.

Issue 70: Dataset structure might reveal information documentationms:CRHorizontalReview

Add text that warns implementers using this specification in selective disclosure schemes that graph structure might reveal information about the entity disclosing the information. For example, knowing that a blank node contains two triples vs. five triples might reveal that the entity that is disclosing the information is a part of a subclass of a population, which might be enough to disclose information beyond what the discloser intended to disclose.

The nature of the canonicalization algorithm inherently correlates its output, i.e., the canonical labels and the sorted order of quads, with the input dataset. This could pose issues, particularly when dealing with datasets containing personal information. For example, even if certain information is removed from the canonicalized dataset for some privacy-respecting reason, there remains the possibility that a third party could infer the omitted data by analyzing the canonicalized dataset. If it is necessary to decouple the canonicalization algorithm's input and output, some suitable post-processing methods for the output of the canonicalization should be performed. This specification has been designed to help make additional processing easier, but other specifications that build on top of this one are responsible for providing any specific details. See the Data Integrity specification for more details about such post-processing methods.

7. Security Considerations

This section is non-normative.

7.1 Dataset Poisoning

The canonicalization algorithm examines every difference in the information connected to blank nodes in order to ensure that each will properly receive its own canonical identifier. This process can be exploited by attackers to construct datasets which are known to take large amounts of computing time to canonicalize, but that do not express useful information or express it using unnecessary complexity. Implementers of the algorithm are expected to add mitigations that will, by default, abort canonicalizing problematic inputs.

Suggested mitigations include, but are not limited to:

Additionally, software that uses implementations of the algorithm can employ best-practice schema validation to reject data that does not meet application requirements, thereby preventing useless poison datasets from being processed. However, such mitigations are application specific and not directly applicable to implementers of the canonicalization algorithm itself.

8. Use Cases

This section is non-normative.

Editor's note

TBD

9. Examples

This section is non-normative.

9.1 Duplicate Paths

This example illustrates a more complicated example where the same paths through blank nodes are duplicated in a graph, but use different blank node identifiers.

The image represents the graph described in the following code block .

Figure 6 An illustration of a graph with duplicated paths.
Image available in SVG .
_:e0 :p1 _:e1 .
_:e1 :p2 "Foo" .
_:e2 :p1 _:e3 .
_:e3 :p2 "Foo" .

The following is a summary of the more detailed execution log found here.

9.2 Double Circle

This example illustrates another complicated example of nodes that are doubly connected in opposite directions.

The image represents the graph described in the following code block .

Figure 7 An illustration of a graph back and forth links to nodes.
Image available in SVG .
_:e0 :next _:e1 .
_:e0 :prev _:e1 .
_:e1 :next _:e0 .
_:e1 :prev _:e0 .

The example is not explored in detail, but the execution log found here shows examples of more complicated pathways through the algorithm

A. A Canonical form of N-Quads

This section defines a canonical form of N-Quads which has a completely specified layout. The grammar for the language remains unchanged.

Canonical N-Quads updates and extends Canonical N-Triples in [N-TRIPLES] to include graphLabel.

While the N-Quads syntax [N-QUADS] allows choices for the representation and layout of RDF data, the canonical form of N-Quads provides a unique syntactic representation of any quad. Each code point can be represented by only one of UCHAR, ECHAR, or unencoded character, where the relevant production allows for a choice in representation. Each quad is represented entirely on a single line with specified white space.

Canonical N-Quads has the following additional constraints on layout:

B. URGNA2012

This section is non-normative.

A previous version of this algorithm has light deployment. For purposes of identification, the algorithm is called the "Universal RDF Graph Canonicalization Algorithm 2012" (URGNA2012), and differs from the stated algorithm in the following ways:

C. Index

C.1 Terms defined by this specification

C.2 Terms defined by reference

D. Changes since the First Public Working Draft of 24 November 2022

This section is non-normative.

E. Acknowledgements

This section is non-normative.

The editors would like to thank Jeremy Carroll for his work on the graph canonicalization problem, Gavin Carothers for providing valuable feedback and testing input for the algorithm defined in this specification, Sir Tim Berners Lee for his thoughts on graph canonicalization over the years, Jesús Arias Fisteus for his work on a similar algorithm.

Members of the RDF Dataset Canonicalization and Hash Working Group Group included Ahamed Azeem, Ahmad Alobaid, Andy Seaborne, Benjamin Goering, Brent Zundel, Dan Brickley, Dan Yamamoto, Dave Longley, David Lehn, Gregg Kellogg, Ivan Herman, Jesse Wright, Kazue Sako, Leonard Rosenthol, Mahmoud Alkhraishi, Manu Sporny, Markus Sabadello, Michael Prorock, Phil Archer, Pierre-Antoine Champin, Sebastian Crane, Ted Thibodeau, Timothée HAUDEBOURG, and Tobias Kuhn.

Editor's note

Acknowledge CCG members.

F. References

F.1 Normative references

[INFRA]
Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/
[N-Quads]
RDF 1.1 N-Quads. Gavin Carothers. W3C. 25 February 2014. W3C Recommendation. URL: https://www.w3.org/TR/n-quads/
[N-TRIPLES]
RDF 1.1 N-Triples. Gavin Carothers; Andy Seaborne. W3C. 25 February 2014. W3C Recommendation. URL: https://www.w3.org/TR/n-triples/
[RDF11-CONCEPTS]
RDF 1.1 Concepts and Abstract Syntax. Richard Cyganiak; David Wood; Markus Lanthaler. W3C. 25 February 2014. W3C Recommendation. URL: https://www.w3.org/TR/rdf11-concepts/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC3987]
Internationalized Resource Identifiers (IRIs). M. Duerst; M. Suignard. IETF. January 2005. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc3987
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174
[Turtle]
RDF 1.1 Turtle. Eric Prud'hommeaux; Gavin Carothers. W3C. 25 February 2014. W3C Recommendation. URL: https://www.w3.org/TR/turtle/
[UNICODE]
The Unicode Standard. Unicode Consortium. URL: https://www.unicode.org/versions/latest/
[XPATH-FUNCTIONS]
XQuery 1.0 and XPath 2.0 Functions and Operators (Second Edition). Ashok Malhotra; Jim Melton; Norman Walsh; Michael Kay. W3C. 14 December 2010. W3C Recommendation. URL: https://www.w3.org/TR/xpath-functions/

F.2 Informative references

[CCG-RDC-FINAL]
RDF Dataset Canonicalization. Dave Longley. W3C. 2022-10-09. CG-FINAL. URL: https://www.w3.org/community/reports/credentials/CG-FINAL-rdf-dataset-canonicalization-20221009/
[DesignIssues-Diff]
Delta: an ontology for the distribution of differences between RDF graphs. Tim Berners-Leee. W3C. 2015-09-25. unofficial. URL: https://www.w3.org/DesignIssues/Diff
[eswc2014Kasten]
A Framework for Iterative Signing of Graph Data on the Web. Andreas Kasten; Ansgar Scherp; Peter Schauß . ISWC 2014. 2014. unofficial. URL: https://doi.org/10.1007/978-3-319-07443-6_11
[Hogan-Canonical-RDF]
Canonical Forms for Isomorphic and Equivalent RDF Graphs: Algorithms for Leaning and Labelling Blank Nodes. Aiden Hogan. ACM Transactions on the Web. 2017-07-17. unofficial. URL: https://aidanhogan.com/docs/rdf-canonicalisation.pdf
[HPL-2003-142]
Signing RDF Graphs. Jeremy J. Carroll. HP Laboratories Bristol. 2003-07-23. unofficial. URL: https://www.hpl.hp.com/techreports/2003/HPL-2003-142.pdf
[RDF11-MT]
RDF 1.1 Semantics. Patrick Hayes; Peter Patel-Schneider. W3C. 25 February 2014. W3C Recommendation. URL: https://www.w3.org/TR/rdf11-mt/
[YAML]
YAML Ain’t Markup Language (YAML™) Version 1.2. Oren Ben-Kiki; Clark Evans; Ingy döt Net. 1 October 2009. URL: http://yaml.org/spec/1.2/spec.html