![]() |
VOOZH | about |
dotnet add package Sagynbaev.Tessera.Chains.Cardano --version 4.0.0
NuGet\Install-Package Sagynbaev.Tessera.Chains.Cardano -Version 4.0.0
<PackageReference Include="Sagynbaev.Tessera.Chains.Cardano" Version="4.0.0" />
<PackageVersion Include="Sagynbaev.Tessera.Chains.Cardano" Version="4.0.0" />Directory.Packages.props
<PackageReference Include="Sagynbaev.Tessera.Chains.Cardano" />Project file
paket add Sagynbaev.Tessera.Chains.Cardano --version 4.0.0
#r "nuget: Sagynbaev.Tessera.Chains.Cardano, 4.0.0"
#:package Sagynbaev.Tessera.Chains.Cardano@4.0.0
#addin nuget:?package=Sagynbaev.Tessera.Chains.Cardano&version=4.0.0Install as a Cake Addin
#tool nuget:?package=Sagynbaev.Tessera.Chains.Cardano&version=4.0.0Install as a Cake Tool
Privacy-preserving, chain-agnostic identity and reputation infrastructure for .NET.
DIDs, signed attestations, selective disclosure via Merkle bundles, Bulletproof-based
predicate proofs over committed values, and on-chain anchoring of attestation roots and
revocation epochs. Plug in any network by implementing IChainAnchor β Solana, Cardano,
EVM, and Stellar adapters are included β plus generic building blocks for permissioned EVM
tokens gated by identity.
π NuGet
π NuGet Downloads
π Build
π .NET
Website: https://tessera-website-sepia.vercel.app/
did:tessera:...).Published on nuget.org under the Sagynbaev. prefix (e.g. the Tessera.Sdk assembly ships as
the Sagynbaev.Tessera.Sdk package); namespaces remain Tessera.*.
| Package (assembly) | Purpose |
|---|---|
Tessera.Sdk |
Entry point for most consumers. High-level Holder, Issuer, Verifier facades. |
Tessera.Core |
DidId, Base58. Zero external dependencies. |
Tessera.Did |
DidDocument, DidService, IDidStore, wallet/channel binding, revocation. |
Tessera.Attestations |
Attestation, AttestationIssuer, MerkleTree, AttestationVerifier, PresentationVerifier, IIssuerRegistry, CredentialProof (incl. CombineCommitments/CombineOpenings for predicates over a sum/difference of commitments), ChainSnapshot. |
Tessera.Cryptography |
Pure-C# secp256k1, Pedersen commitments, Bulletproofs (no external deps). |
Tessera.Signing |
Production Ed25519 (NSec / libsodium). Drop-in Ed25519Verifier and Ed25519IssuerSigner. |
Tessera.EntityFrameworkCore |
EF Core IDidStore and IIssuerRegistry over any relational provider (Postgres, SQL Server, SQLite). |
Tessera.Chains.Abstractions |
IChainAnchor + IAllowlistGateway + DidHash β chain-agnostic interfaces. |
Tessera.Chains.Solana |
Solana adapter targeting the identity-registry Anchor program. |
Tessera.Chains.Cardano |
Cardano adapter (CardanoSharp + Blockfrost): CardanoChainAnchor targeting the Aiken identity-registry Plutus V3 validators (preprod), with a metadata-mode fallback. |
Tessera.Chains.Stellar |
Stellar adapter scaffold targeting a Soroban anchor contract. |
Tessera.Chains.Midnight |
Midnight adapter scaffold β Compact contract + transaction layer pending (reads report no anchor, writes throw NotSupported). |
Tessera.Chains.Evm |
Generic EVM adapter (Nethereum): EvmChainAnchor + EvmAllowlistGateway, any chainId/RPC. |
Tessera.Sources.Sumsub |
Layer-2 plugin: Sumsub KYC β kyc_verified / jurisdiction attestations. |
Tessera.Sources.XRoad |
Layer-2 plugin: X-Road government registry β residency / property / encumbrance. |
Tessera.Sources.Bitcoin |
Layer-2 plugin: proven control of Bitcoin addresses (BIP-137 signed challenge) β btc_control (address count only) + Pedersen-committed btc_balance / btc_hodl_age, each bound to a point-in-time chain snapshot (height/hash/time). Esplora (mempool.space / blockstream.info) provider. |
π 4.0.0 β the security-hardening release. On top of the v3.2.0 baseline (authenticated holder presentations, fail-closed revocation, address-bound wallet binding, authenticated on-chain anchors β see Security), a multi-round audit (no Critical findings) made
Tessera.Cryptographyconstant-time (limb-based arithmetic + a branchless scalar multiplication, cross-checked against an independent BouncyCastle oracle), rejected non-canonical encodings, added controller-authenticated wallet binding, and removed the legacy duplicate crypto stack (breaking). It is still from-scratch managed code, so an external crypto audit remains recommended. Threat model, findings & limitations: Β· full changelog: .
Tessera/
βββ src/
β βββ Tessera.Core/ DidId, Base58
β βββ Tessera.Did/ DID model + service
β βββ Tessera.Channels/ channel binding (internal, not packaged)
β βββ Tessera.Attestations/ Attestations + Merkle + CredentialProof + schema registry
β βββ Tessera.Cryptography/ secp256k1 + Bulletproofs
β βββ Tessera.Signing/ Ed25519 (NSec)
β βββ Tessera.EntityFrameworkCore/ Postgres/SQL Server/SQLite stores
β βββ Tessera.Chains.Abstractions/ IChainAnchor, IAllowlistGateway, DidHash
β βββ Tessera.Chains.Solana/ Solana adapter (Solnet)
β βββ Tessera.Chains.Cardano/ Cardano adapter (CardanoSharp + Blockfrost, Aiken Plutus V3)
β βββ Tessera.Chains.Evm/ Generic EVM adapter (Nethereum) + allowlist gateway
β βββ Tessera.Chains.Stellar/ Stellar adapter scaffold
β βββ Tessera.Chains.Midnight/ Midnight adapter scaffold (Compact + tx layer pending)
β βββ Tessera.Sdk/ Holder, Issuer, Verifier, IssuancePipeline, policy
β βββ Tessera.Sources.Sumsub/ Layer-2 plugin: Sumsub KYC
β βββ Tessera.Sources.XRoad/ Layer-2 plugin: X-Road government registry
β βββ Tessera.Sources.Bitcoin/ Layer-2 plugin: proof of Bitcoin control + balance (NBitcoin)
β
βββ chains/
β βββ solana/programs/identity-registry/ Anchor program (adapter: complete)
β βββ evm/ Hardhat: IdentityRegistry, Allowlist, PermissionedToken
β βββ cardano/contracts/identity-registry/ Aiken validators (Plutus V3, preprod; adapter: complete)
β βββ stellar/contracts/attestation-verifier/ Soroban contract (adapter: in progress)
β
βββ examples/
β βββ PrivacyApps/ ConfidentialTransfer, SealedBidAuction, PrivateVoting
β βββ PermissionedToken/ Layer-3 reference: compliance flow end-to-end
β βββ CardanoCreditLine/ Income attestation β anchor on Cardano preprod β verify
β βββ BitcoinCreditLine/ Proof of Bitcoin balance β₯ 1 BTC β anchor on Cardano β verify
β
βββ Tessera/ v2.x monolith β kept for backward compat
βββ docs/
βββ architecture.md layering, packages, on-chain/off-chain boundary
βββ security-audit-readiness.md audit dossier, threat model, known limitations
See for the on-chain/off-chain boundary and the package dependency rules.
The SDK is the entry point. The three facades cover the three roles in any attestation flow: holder, issuer, verifier.
Package IDs are prefixed
Sagynbaev.on nuget.org (the bareTessera/Tessera.*IDs are owned by other authors). The assembly names and namespaces are unchanged β you still writeusing Tessera.Sdk;. The single meta-package isSagynbaev.Tessera.
dotnet add package Sagynbaev.Tessera.Sdk
dotnet add package Sagynbaev.Tessera.Signing
# pick the chain adapter you need:
dotnet add package Sagynbaev.Tessera.Chains.Cardano # or Sagynbaev.Tessera.Chains.Solana / .Chains.Evm
# pick a store (or use the in-memory one for tests):
dotnet add package Sagynbaev.Tessera.EntityFrameworkCore
using Tessera.Sdk;
using Tessera.Signing;
using Tessera.Did;
// One-time keypair for the human/agent who controls this DID.
var (controllerPriv, controllerPub) = Ed25519.GenerateKeypair();
var holder = await Holder.CreateAsync(controllerPub, new HolderOptions
{
Store = new InMemoryDidStore(), // or EfCoreDidStore for Postgres
SignatureVerifier = new Ed25519Verifier(),
ChainAnchor = solanaAnchor, // optional; null = offline mode
});
// `holder.Did` is "did:tessera:<base58(sha256(pubkey||"v1"))>" β deterministic, not chosen.
// Later: accept an issuer-signed attestation, anchor the new root on-chain.
holder.AcceptAttestation(attestationFromIssuer);
await holder.AnchorRootAsync();
// Build a presentation for a relying app, disclosing only what it needs.
// The holder AUTHENTICATES it by signing the canonical challenge with the controller
// key; the matching public key is embedded so the verifier re-derives the DID and
// checks the signature with no store lookup.
var presentation = holder.BuildSignedPresentation(
verifier: new DidId("did:tessera:my-relying-app"),
attestationTypes: new[] { "phone_verified" },
sessionNonce: RandomBytes(16),
asOfRevocationEpoch: 0,
chain: "solana",
signChallenge: ch => Ed25519.Sign(controllerPriv, ch.Span));
using var signer = new Ed25519IssuerSigner(issuerPrivateKey);
var issuer = new Issuer(new DidId("did:tessera:my-issuer-service"), signer);
var attestation = issuer.Issue(
type: AttestationTypes.PhoneVerified,
subject: subjectDid,
payload: new AttestationPayload { Method = "twilio_v2" },
validity: TimeSpan.FromDays(365));
// Register yourself once so verifiers can find you:
await issuerRegistry.RegisterAsync(issuer.BuildRegistryRecord(
schemaUri: "https://schemas.zkp/attestation/v1"));
var verifier = new Verifier(new VerifierOptions
{
IssuerRegistry = issuerRegistry,
SignatureVerifier = new Ed25519Verifier(),
ChainAnchor = solanaAnchor,
});
var result = await verifier.VerifyPresentationAsync(presentation, new VerificationPolicy
{
ExpectedVerifier = new DidId("did:tessera:my-relying-app"),
ExpectedSessionNonce = nonceIssuedAtSessionStart,
RequireCurrentRevocationEpoch = true,
});
if (!result.Valid)
return Unauthorized(result.Reason); // e.g. "verifier_mismatch", "revocation_stale"
For attestations carrying a Pedersen commitment, the holder proves a predicate
(e.g. income β₯ 50,000) without revealing the value. Bulletproofs on secp256k1,
implemented from scratch. The proof is bound to the attestation's commitment, so it
cannot be reused for a different value:
using Tessera.Attestations;
var cp = new CredentialProof();
// Issuer commits to the value in the attestation; the holder keeps the opening.
var (commitment, opening) = cp.CommitValue(85_000);
var attestation = issuer.Issue(AttestationTypes.Accredited, subjectDid,
new AttestationPayload { Method = "payroll", Commitment = commitment });
// Holder proves income β₯ 50,000, bound to that commitment.
var bundle = cp.ProveBoundMinimum(actualValue: 85_000, minimumRequired: 50_000, opening, label: "income");
// Verifier confirms the proof is valid AND about this attestation's committed value.
bool valid = cp.VerifyBound(commitment, bundle); // learns only "income β₯ 50,000"
The verifier policy enforces this declaratively via PredicateRequirement (see
). The unbound ProveMinimum/Verify remain
available as a standalone primitive but are not accepted by the policy.
IDidStore and IIssuerRegistry are pluggable. Two implementations ship:
InMemoryDidStore / InMemoryIssuerRegistry β for tests and offline dev.EfCoreDidStore / EfCoreIssuerRegistry β EF Core 8, provider-agnostic.Postgres example:
services.AddDbContext<TesseraDbContext>(opts =>
opts.UseNpgsql(connectionString));
services.AddScoped<IDidStore, EfCoreDidStore>();
services.AddScoped<IIssuerRegistry, EfCoreIssuerRegistry>();
services.AddSingleton<ISignatureVerifier, Ed25519Verifier>();
Generate migrations against your chosen provider:
dotnet ef migrations add InitialTessera --project Tessera.EntityFrameworkCore
The on-chain layer stores only Merkle attestation roots and revocation epochs. DID documents, attestations, and proofs are never written on-chain.
| Chain | Status | Code |
|---|---|---|
| Solana | Adapter complete; one-command devnet deploy () β the env-gated smoke suite runs live against the deployed program; record the id in | |
| EVM | Adapter complete; contracts + ABI checked in | |
| Cardano | Adapter complete; Aiken Plutus V3 validators (preprod) β aiken check green, blueprint checked in; preprod script addresses in |
|
| Stellar | Adapter scaffold; anchor contract pending | |
| Midnight | Adapter scaffold; Compact contract + tx layer pending (mainnet is live) |
The Solana adapter speaks to a minimal Anchor program whose four core anchoring instructions are
register_did, update_root, bump_revocation, register_issuer (plus admin-gated
initialize / deactivate_issuer). The EVM adapter
(Tessera.Chains.Evm) drives the equivalent
on any EVM network β chainId/RPC/contract are pure configuration. The Cardano adapter
(Tessera.Chains.Cardano) drives the same four operations under eUTXO via the Aiken
Plutus V3 validators
(state-thread tokens + inline datums, preprod), with a metadata-mode fallback for demos. Off-chain
verification stays in C#. Native Midnight integration is planned β see Roadmap.
Generic building blocks let any permissioned EVM token gate ownership on Tessera identity, with zero token/provider specifics in the core:
IAllowlistGateway + EvmAllowlistGateway reflect an off-chain verification decision onto an
on-chain transfer-restriction contract (Add/Revoke), compatible with a simple allowlist or
an ERC-3643/T-REX whitelist module via configuration.IssuancePipeline turns pluggable IAttestationSources (e.g. Sumsub, X-Road, Bitcoin) into signed
attestations; VerificationPolicy declares required types + predicate (range-proof) rules.assembles these into the target scenario β a permissioned BEP-20 () whose transfers are gated by the allowlist. Its end-to-end test walks KYC/registry onboarding β DID + attestations β presentation β policy β allowlist admission β token ownership, then revokes KYC and shows transfers are blocked. See for the audit dossier and known limitations.
v3 is a breaking cut from the v2.x monolith. v2.x consumers keep working until they upgrade.
| v2 type | v3 replacement |
|---|---|
Tessera.Core.Zkp (HMAC equality) |
Removed. Use CredentialProof for ZK predicates. |
Tessera.Interfaces.IBlockchain |
Tessera.Chains.IChainAnchor. |
Tessera.Integration.Stellar.* |
Tessera.Chains.Stellar. |
Tessera.Crypto.* |
Tessera.Cryptography. |
Tessera.Privacy.CredentialProof |
Tessera.Attestations.CredentialProof. |
The current release (4.0.0) is the security-hardening release β constant-time cryptography, canonical encodings, controller-authenticated wallet binding, and removal of the legacy duplicate crypto stack β on top of the v3.2.0 baseline. The guarantees that matter at the trust boundary:
PresentationBinding.HolderPublicKey, 32-byte Ed25519) and a signature over a
canonical PresentationChallenge (verifier, session nonce, revocation epoch, chain,
CreatedAt, disclosed leaf hashes). The verifier confirms
DidId.FromControllerKey(HolderPublicKey) == Holder and checks the signature β so audience,
nonce, epoch and freshness are all holder-authenticated, not just plaintext fields. Build with
Holder.BuildSignedPresentation(..., signChallenge: ch => Ed25519.Sign(controllerPriv, ch.Span)),
or split via BuildPresentationChallenge + BuildPresentation(..., holderSignature, createdAt)
for out-of-band / hardware signers.RequireCurrentRevocationEpoch additionally demands a reachable anchor and an EXACT match to
the current epoch (it no longer silently skips when ExpectedAnchorRoot is supplied). A
presentation freshness window (MaxPresentationAge / MaxClockSkew, both authenticated via
the signed CreatedAt) bounds replay.IWalletControlVerifier (the default covers
Solana base58(pubkey) and fails closed on chains it does not understand). BuildWalletChallenge
binds the wallet pubkey, and binding nonces are single-use through an INonceStore. The
authenticated BindWalletAsync overload additionally requires a DID-controller signature over
BuildWalletBindAuthChallenge, so a wallet owner cannot attach their wallet to another principal's
DID. DidService.GetActiveAsync enforces revocation on resolve; pepper providers reject all-zero /
low-entropy peppers.Tessera.Cryptography secp256k1 is
constant-time (fixed 4Γ64-bit limb arithmetic; a fixed-iteration, branchless Point.ScalarMul
over complete add/double formulas), so the secret scalar does not leak through timing. Point and
proof-scalar deserialization rejects non-canonical encodings (x β₯ p, s β₯ n) rather than
reducing them, closing byte-malleability of commitments/proofs. Both are cross-checked against an
independent BouncyCastle oracle. The issuer registry refuses to overwrite an existing issuer's
public key, and attestation verification supports an expiry cap (MaxAttestationAge /
RequireExpiry).externalUserId to equal the
subject DID; X-Road uses server-asserted identifiers verified against the request. Both clients
require HTTPS.IdentityRegistry.registerDid requires a controller
ECDSA signature bound to (didHash, root, chainid, contract); the C# adapter signs it and
checks receipt.Status on every write. Solana register_issuer is admin-gated via a
RegistryConfig PDA (initialize(admin) / deactivate_issuer); the adapter fails closed on
RPC errors and checks the owning program + discriminator. Cardano metadata-mode reads
authenticate the controller (tx input address + embedded controller signature over
did_hashβrootβepoch) and the Aiken issuer_registry is governance-gated.attestation-verifier was redesigned to Ed25519
public-key verification with admin initialize / set_issuer; the trusted issuer key lives in
contract storage. The HMAC secret is no longer a caller-supplied argument β any --hmac_key /
ZKP_HMAC_KEY usage is removed.examples/PrivacyApps, voting uses a 1-bit range proof ({0,1}); the
sealed-bid auction enforces both bounds; the confidential transfer checks Pedersen balance
conservation (amount + change == sender balance) in addition to the range proofs.Caveats: Tessera.Cryptography is now constant-time and rejects non-canonical encodings, but it
is still from-scratch managed code β an external cryptography audit remains recommended, and two
BREAKING wire-format hardening items (length-prefixed FiatβShamir transcript labels; RFC-6962 Merkle
odd-node handling) plus a type-tagged claim-canonicalization format are deferred. The Anchor (Solana),
Aiken (Cardano) and Solidity (EVM) contract changes above are source-level: they require the
respective toolchain build (anchor build / aiken build + regenerated plutus.json / Hardhat
compile) and redeploy to take effect on a live network. See
for the full threat model.
Planned, not yet shipped:
Tessera.Cryptography β now constant-time and BouncyCastle
cross-checked, but still self-implemented; the audit dossier is already public in
.MIT.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.0.0 | 38 | 6/17/2026 |
| 3.3.1 | 45 | 6/17/2026 |
| 3.3.0 | 86 | 6/14/2026 |
| 3.3.0-preview.2 | 46 | 6/14/2026 |
| 3.3.0-preview.1 | 46 | 6/14/2026 |
| 3.2.0 | 81 | 6/13/2026 |