Skip to content
Proposed specification for review. We are seeking feedback on key architectural choices in the industry consultation.

pdtf-core (Rust)

Native Rust implementation of the PDTF 2.0 core library. Designed for performance-critical workloads and as the foundation for cross-language bindings.

[dependencies]
pdtf-core = { git = "https://github.com/property-data-standards-co/core-rs" }

Repository: property-data-standards-co/core-rs


core-rs/
├── crates/pdtf-core/ # Core Rust library
│ ├── src/
│ │ ├── keys/ # Ed25519 key generation, did:key encoding
│ │ ├── signer/ # DataIntegrityProof creation (eddsa-jcs-2022)
│ │ ├── validator/ # Proof verification
│ │ ├── did/ # DID resolution (did:key, did:web, URN)
│ │ ├── status/ # Bitstring Status List
│ │ └── tir/ # Trusted Issuer Registry client
│ └── tests/
│ └── cross_language.rs # Shared vector validation
├── bindings/
│ ├── python/ # PyO3 bindings
│ ├── dotnet-ffi/ # C-ABI FFI layer
│ └── dotnet/ # C# wrapper + tests
└── test-vectors/
└── vectors.json # Shared cross-language test vectors

Ed25519 key generation, did:key derivation, and multibase encoding.

use pdtf_core::keys::ed25519::{generate_keypair, derive_did_key, public_key_to_multibase};
let kp = generate_keypair();
let did = derive_did_key(kp.verifying_key.as_bytes())?;
// did:key:z6Mk...
let multibase = public_key_to_multibase(kp.verifying_key.as_bytes())?;
// z6Mk...
// Round-trip
let recovered = did_key_to_public_key(&did)?;
assert_eq!(recovered.as_slice(), kp.verifying_key.as_bytes());

Create and verify DataIntegrityProof using eddsa-jcs-2022.

use pdtf_core::signer::proof::{create_proof, verify_proof, CreateProofOptions};
// Sign
let proof = create_proof(CreateProofOptions {
document: &vc,
key_id: "my-key",
verification_method: "did:key:z6Mk...#z6Mk...",
key_provider: &my_key_provider,
created: None, // defaults to now
}).await?;
// Verify
let valid = verify_proof(&signed_vc, &public_key_bytes);

Algorithm (identical to TypeScript):

  1. JCS-canonicalize proof options → SHA-256
  2. JCS-canonicalize document (no proof) → SHA-256
  3. Concatenate hashes (64 bytes)
  4. Ed25519 sign (raw bytes)
  5. base58-btc encode with z prefix

DID resolution for did:key, did:web, and urn:pdtf:*.

use pdtf_core::did::resolver::resolve;
let doc = resolve("did:key:z6Mk...").await?;
let doc = resolve("did:web:example.com:transactions:abc123").await?;

Bitstring Status List — create, encode (gzip+base64), decode, and check bits.

use pdtf_core::status::bitstring::*;
let mut list = create_status_list(131_072)?;
set_bit(&mut list, 42)?;
let encoded = encode_status_list(&list)?; // base64(gzip(bytes))
let decoded = decode_status_list(&encoded)?;
assert!(get_bit(&decoded, 42)?);

Trusted Issuer Registry — load, validate issuer entries, path matching.

use pdtf_core::tir::verify::verify_tir;
use pdtf_core::tir::path_match::path_matches;
// Path matching
assert!(path_matches("Property:/energyEfficiency/*", "Property:/energyEfficiency/certificate"));
assert!(!path_matches("Property:*", "Title:/registerExtract/entries"));
// Full TIR verification
let result = verify_tir(&registry, "did:key:z6Mk...", &["Property:/energyEfficiency/certificate"]);
assert!(result.trusted);

84 tests across the workspace:

CrateTestsCoverage
pdtf-core (unit)67Keys, signing, verification, DID, status, TIR, structure validation
pdtf-core (integration)6Cross-language vector validation against TypeScript
dotnet-ffi8FFI function correctness, error handling
doc-tests3Inline documentation examples
Terminal window
cargo test --workspace

The Rust tests consume test-vectors/vectors.json generated by the TypeScript reference implementation using a fixed Ed25519 seed. This proves byte-level interoperability:

TestWhat’s proven
crosslang_key_derivationIdentical did:key and multibase encoding
crosslang_signing_produces_identical_proofsSame JCS + SHA-256 + Ed25519 = identical proof values
crosslang_verificationRust verifies TypeScript-signed VCs (valid, tampered, wrong key)
crosslang_status_list_empty_roundtripDecoded status lists are byte-identical
crosslang_status_list_operationsSet/check operations produce same results
crosslang_tir_path_matchingWildcard pattern matching is consistent

Note: Gzip implementations may produce different compressed byte sequences, so status list tests compare decoded (logical) content rather than raw base64 strings.