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 testAll 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 RDFCanonizeBenchBaseline 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