Contents

kingpin-apps/swift-rdf-canonize

A pure-Swift implementation of RDF Dataset Canonicalization

Quickstart

import RDFCanonize

let canonical = try RDFCanonize.canonicalize(nquads: """
_:b1 <http://example.org/p> _:b2 .
_:b2 <http://example.org/q> "leaf" .
""")
// → "_:c14n0 <http://example.org/p> _:c14n1 .\n_:c14n1 <http://example.org/q> \"leaf\" .\n"

Blank-node labels are reassigned to :c14n0, :c14n1, …; quads are emitted in lexicographic order; duplicate quads collapse.

If you already have parsed quads — for example from a JSON-LD toRdf pass — skip the parser:

let canonical = try RDFCanonize.canonicalize(quads: quads)

Installation

dependencies: [
    .package(url: "https://github.com/Kingpin-Apps/swift-rdf-canonize.git", from: "0.2.0"),
],

Then add RDFCanonize to your target's dependencies:

.target(name: "MyApp", dependencies: [
    .product(name: "RDFCanonize", package: "swift-rdf-canonize"),
]),

Platforms

iOS 14+, macOS 13+, watchOS 9+, tvOS 14+, visionOS 1+, and Linux on Swift 6.0+. Strict-concurrency clean (compiled at swiftLanguageModes: [.v6]).

Hash algorithm

SHA-256 by default, as the spec stipulates. Pass hashAlgorithm: .sha384 to opt into the alternative defined in RDFC-1.0 §6.

let canonical = try RDFCanonize.canonicalize(
    nquads: input,
    hashAlgorithm: .sha384
)

Poison-graph defense

Trusted inputs canonicalize without a bound — that's the default (workFactor: .max). Pass a finite workFactor to reject inputs whose symmetric topology would explode the N-Degree Hash recursion. The deep-iteration budget is nonUniqueBlankNodeCount ^ workFactor; exceeding it throws RDFCanonize.CanonicalizeError.iterationLimitExceeded.

do {
    let canonical = try RDFCanonize.canonicalize(
        nquads: untrustedInput,
        workFactor: 1  // tight bound — rejects poison cliques
    )
} catch RDFCanonize.CanonicalizeError.iterationLimitExceeded {
    // Input was symmetric beyond what we'll spend cycles on.
}

Conformance suite

The W3C rdf-canon test suite is checked in as a git submodule:

git submodule update --init
swift test

All 86 manifest entries pass on every run. The harness lives in Tests/RDFCanonizeTests/W3CSuite/; each conformance entry becomes a parameterized Swift Testing case mapping the manifest's computationalComplexity tag to the appropriate workFactor.

Benchmarks

swift run -c release RDFCanonizeBench

Baseline numbers for the 0.2.0 release are recorded in CHANGELOG.md. Scenarios cover linear-chain datasets from 100 to 10,000 quads at 0%, 10%, and 100% blank-node density, plus a symmetric-cluster generator that exercises the n-degree hash recursion.

Use cases

Canonicalization is the prerequisite for any deterministic hash or signature over linked-data documents:

  • CIP-100 Cardano governance metadata signed by SPOs, dReps, and

CC members.

  • W3C Verifiable Credentials and other linked-data proofs.
  • DID document integrity hashes.
  • Any audit trail or merkleization scheme over JSON-LD / RDF datasets.

When called from JSON-LD, the canonicalize(quads:) entry point accepts already-materialized quads to avoid an unnecessary N-Quads round-trip.

License

MIT. See LICENSE.

Package Metadata

Repository: kingpin-apps/swift-rdf-canonize

Default branch: main

README: README.md