cardanowall — Rust SDK for the CIP-309 Proof-of-Existence standard
cardanowall is the Rust implementation of the CIP-309 Proof-of-Existence (PoE)
toolkit: a standalone structural validator, a public verifier, a recipient
verifier with sealed-PoE decryption, and a gateway-agnostic HTTP client. It is a
byte-parity sibling of the TypeScript (@cardanowall/sdk-ts) and Python
(cardanowall-sdk) SDKs — it independently reproduces the same canonical-CBOR
bytes, validation verdicts, and cryptographic outputs, proven against the same
shared cross-implementation test vectors.
Nothing in this crate trusts a publisher or an issuer server. A proof verifies
from a Cardano transaction's metadata, an optional copy of the content bytes, and
a public blockchain explorer. The HTTP layer is blocking (reqwest in blocking
mode) and secure-by-default: every outbound call flows through one egress point
that enforces a protocol/method allowlist, a deny-host policy, bounded response
bodies and timeouts, and an SSRF guard for user-supplied URLs.
The companion cardanowall CLI binary is a separate crate built on top of this
SDK.
What it is
- A standalone verifier with three roles, all service-independent:
- Structural validator —
poe_standard::validate_poe_record, a pure function over canonical-CBOR bytes. No I/O, no signatures, no decryption. - Public verifier —
verifier::verify_txresolves a transaction, extracts the label-309 record, validates it structurally, and verifies record-level signatures. - Recipient verifier — the public verifier plus an X25519 private key: decrypts a sealed PoE and recomputes plaintext hashes.
- Structural validator —
- A gateway-agnostic client (
client::CardanowallClient) for any CIP-309 gateway: you pass an explicit base URL and an optional opaque bearer key. - The cryptographic building blocks — hash, KDF, COSE, sealed-PoE, recipient encoding, Merkle, and seed-derived identity helpers.
Install
Pre-1.0 and not yet published to crates.io. Build from the workspace, or depend on it by path/git:
# Cargo.toml — by git, until published
[]
= { = "https://github.com/cardanowall/cip309-rs" }
Once published to crates.io the line will be:
# once published
[]
= "0.1"
The crate requires the OS CSPRNG for the secure-by-default sealed-PoE wrap and
uses rustls for TLS (no system OpenSSL needed).
Quick start
Hash content (byte-identical across the SDK family)
use ;
use hex;
let digests = dual_hash;
println!;
println!;
For large or streamed input, hash::dual_hash_stream computes both digests
without holding the whole input in memory.
Validate a record structurally (pure, no I/O)
use ;
// `record_bytes` is the canonical-CBOR record, reassembled from the
// label-309 bytes-chunk array (records are tag-259-wrapped on the wire).
match validate_poe_record
Verify a transaction end to end (public verifier)
use ;
let mut input = new; // lowercase tx hash, no 0x
input.cardano_gateway_chain = Some;
let report = verify_tx;
match report.verdict
assert!;
verify_tx never panics: a malformed record, a missing label-309 entry, or a
gateway failure each produce a VerifyReport with the corresponding verdict and
exit code (0 ok, 1 integrity, 2 network, 3 insufficient depth). The
report carries a per-call HTTP audit trail in report.http_calls.
Recipient verifier: decrypt a sealed PoE
VerifyTxInput accepts out-of-band recipient keys; the verifier then decrypts
the sealed item and recomputes the plaintext hashes as part of the verdict:
use ;
let mut input = new;
input.cardano_gateway_chain = Some;
input.decryption = Some;
let report = verify_tx;
assert_eq!;
Sign a record off-host from a master seed
The seed-derived signer holds the Ed25519 secret in-memory and exposes only the public key and the 64-byte signature, so the gateway never sees a private key:
use Signer;
use signer_from_seed;
let seed = ; // the 32-byte master identity seed
let signer = signer_from_seed?; // SeedDeriveError on wrong length
let pubkey = signer.signer_pubkey; // raw 32-byte Ed25519 public key
let signature = signer.sign?; // 64-byte Ed25519 signature
# Ok::
seed_derive::derive_ed25519_keypair, derive_x25519_keypair, and
derive_mlkem768x25519_keypair (hybrid post-quantum X-Wing) expose the same
deterministic identity keys directly.
Talk to a gateway (any CIP-309 deployment)
The client targets no default host. You name the gateway with an explicit
base_url; the optional api_key is an opaque bearer forwarded verbatim as
Authorization: Bearer … (never validated or parsed). With no key the client is
anonymous and read-only.
use ;
let client = new?;
let balance = client.account.balance?; // AccountBalance
let record = client.records.get?; // RecordResource, by tx hash
# Ok::
cardanowall.com is one example deployment; this SDK works against any gateway
that implements the CIP-309 surface. A missing or empty base_url is the one
illegal config and raises InvalidClientConfigError from the constructor.
API overview
| Module | Surface |
|---|---|
hash |
sha256, blake2b256, dual_hash, dual_hash_stream; SHA2_256_ID, BLAKE2B_256_ID |
poe_standard |
validate_poe_record, encode_poe_record, encode_record_body_for_signing, chunk_bytes / bytes_chunk_array_concat, chunk_uri / reconstruct_chunked_uri; PoeRecord, ValidateResult, ErrorCode |
verifier |
verify_tx, VerifyTxInput, VerifyReport, Verdict, ExitCode, Profile, Decryption; verify_record_signatures, extract_label_309_metadata, resolve_cardano_tx, verify_report_to_dict |
client |
CardanowallClient, CardanowallClientConfig; namespaces poe() / records() / inbox() / account(); Signer, off-host signing helpers, HttpError, ProblemDetails |
seed_derive |
signer_from_seed, derive_ed25519_keypair, derive_x25519_keypair, derive_mlkem768x25519_keypair, derive_bookmark_key |
sealed_poe |
ecies_sealed_poe_wrap_secure, ecies_sealed_poe_unwrap, ecies_sealed_poe_trial_decrypt, envelope/slots/kem/aead |
recipient |
encode_age_x25519_recipient, encode_age_xwing_recipient, parse_age_recipient, bech32 helpers |
merkle |
merkle_root, merkle_inclusion_proof, verify_inclusion, encode_leaves_list / decode_leaves_list |
kdf |
hkdf_sha256 |
cbor, cose, hex, ids, webhooks |
canonical CBOR, COSE_Sign1, hex, wire identifiers, webhook signature verification |
The crate denies unsafe_code and missing docs; cargo doc is the exhaustive,
always-current reference.
Conformance profiles
The verifier reports a record at the highest profile it could check, in strict
superset order: core (hash / uris / merkle) → signed (adds record-level
sigs[]) → sealed (adds the enc envelope structure) → recipient-sealed
(adds byte-level decryption with a recipient key, the default). A lower-profile
verifier that meets a higher-profile field emits an info issue and continues; it
never reports the record invalid.
Cross-implementation parity
This crate is a byte-parity twin of @cardanowall/sdk-ts and
cardanowall-sdk (Python). The three implementations are tested against the same
shared known-answer-test vectors and a captured mainnet record corpus:
- the canonical CBOR of an encoded record is identical bit-for-bit;
validate_poe_recordreturns the same verdict and error codes;verify_tx's report serialises (viaverify_report_to_dict) to the byte-identical JSON the TS and Python SDKs emit for the same transaction;- the seed-derivation
infolabels and outputs match, so one master seed yields the same identity keys in every SDK; - the sealed-PoE wrap/unwrap and the X-Wing (ML-KEM-768 + X25519) hybrid KEM produce identical wire bytes.
Picking Rust, TypeScript, or Python is an ergonomics decision, not a compatibility one.
Standard and service independence
A CIP-309 proof is verifiable with no cooperation from whoever published it:
- fetch the transaction's label-309 metadata from any public Cardano explorer;
- reassemble and structurally validate the record (
validate_poe_record); - verify record-level signatures and, for a sealed PoE, decrypt with the recipient key and recompute plaintext hashes.
verify_tx performs all three. No issuer server is contacted, and a deny-host
policy lets a verifier prove this by blocking any vendor host outright. The
records are stored on-chain as a CBOR bytes-chunk array under metadata label 309
(tag-259-wrapped); reassemble before validating — the helpers in poe_standard
do this for you.
Relation to the other packages
@cardanowall/crypto-core— closed-catalogue cryptographic primitives (hash, KDF, signature, KEM, AEAD, CBOR, COSE, sealed-PoE, Merkle, recipient encoding, seed derivation). The portable building blocks.@cardanowall/poe-standard— the CIP-309 wire-format library: record schema, canonical-CBOR encoder, pure structural validator, error-code catalogue.@cardanowall/sdk-ts— the browser + Node TypeScript SDK: verifier, agnostic client, off-host signing, seed-derived identity helpers.cardanowall-sdk— the Python SDK: a byte-identical parity twin ofsdk-ts.cardanowall(this crate) — the Rust SDK: the byte-parity twin in Rust, blocking HTTP, secure-by-default egress. ThecardanowallCLI is a separate crate built on it.
License
Apache-2.0.