asagynbaev/ZkpSharp
ZkpSharp is a .NET library for implementing Zero-Knowledge Proofs (ZKP). This library allows you to securely prove certain information (such as age or balance) without revealing the actual data. It uses cryptographic hashes and salts to ensure privacy and security.
ZkpSharp
A .NET library for Zero-Knowledge Proofs with Stellar Soroban blockchain integration. Prove facts about private data (age, balance, membership, range, time conditions) without revealing the data itself.
Features
Privacy proofs (HMAC-based commitment schemes) -- fast, lightweight:
- Proof of Age, Balance, Membership, Range, Time Condition
- HMAC-SHA256 with cryptographic salt
True zero-knowledge proofs (Bulletproofs on secp256k1) -- mathematically sound:
- Real Pedersen commitments:
C = v*G + r*Hon the secp256k1 curve - Inner product argument for O(log n) proof size (~690 bytes for 64-bit range)
- Fiat-Shamir transcript for non-interactive proofs
- Implemented from scratch in pure C# -- zero external crypto dependencies
Privacy SDK -- ready-to-use solutions for real-world scenarios:
- Confidential transfers (hide amounts, prove solvency)
- Sealed-bid auctions (commit-reveal with range proof)
- Private voting (anonymous ballots with verifiable tally)
- Credential verification (prove income, credit score, age meets threshold without revealing value)
Stellar blockchain integration:
- Soroban smart contract: full HMAC verification on-chain, structural validation for Bulletproofs
SorobanTransactionBuilderfor XDR constructionStellarBlockchainhigh-level API for both HMAC and ZK on-chain flows
Installation
dotnet add package ZkpSharpQuick start
using ZkpSharp.Core;
using ZkpSharp.Security;
var proofProvider = new ProofProvider("your-base64-encoded-32-byte-key");
var zkp = new Zkp(proofProvider);
// Prove age >= 18 without revealing birthdate
var (proof, salt) = zkp.ProveAge(new DateTime(1995, 3, 15));
bool valid = zkp.VerifyAge(proof, new DateTime(1995, 3, 15), salt);
// Prove sufficient balance without revealing actual amount
var (bProof, bSalt) = zkp.ProveBalance(1000.0, 500.0);
bool bValid = zkp.VerifyBalance(bProof, 500.0, bSalt, 1000.0);For Bulletproofs (true ZKP):
using ZkpSharp.Security;
var zkProvider = new BulletproofsProvider();
var (proof, commitment) = zkProvider.ProveRange(value: 42, min: 0, max: 100);
bool valid = zkProvider.VerifyRange(proof, commitment, min: 0, max: 100);For Privacy SDK (real-world scenarios):
using ZkpSharp.Privacy;
// Confidential transfer -- amount hidden, proven valid
var ct = new ConfidentialTransfer();
var transfer = ct.CreateTransfer(senderBalance: 10000, transferAmount: 2500);
bool valid = ct.VerifyTransfer(transfer);
// Sealed-bid auction
var auction = new SealedBidAuction(minBid: 100, maxBid: 50000);
var (bid, secret) = auction.PlaceBid(7500);
bool bidValid = auction.VerifyBid(bid);
long? revealed = auction.RevealBid(bid, secret); // 7500See QUICKSTART.md for a complete walkthrough including Stellar integration.
API
Zkp -- HMAC-based privacy proofs
| Method | Description |
|---|---|
ProveAge(DateTime dateOfBirth) |
Prove age >= 18 (configurable) |
VerifyAge(string proof, DateTime dateOfBirth, string salt) |
Verify age proof |
ProveBalance(double balance, double requestedAmount) |
Prove balance >= requested |
VerifyBalance(string proof, double requestedAmount, string salt, double balance) |
Verify balance proof |
ProveMembership(string value, string[] validValues) |
Prove value is in set |
VerifyMembership(string proof, string value, string salt, string[] validValues) |
Verify membership proof |
ProveRange(double value, double minValue, double maxValue) |
Prove value is in range |
VerifyRange(string proof, double minValue, double maxValue, double value, string salt) |
Verify range proof |
ProveTimeCondition(DateTime eventDate, DateTime conditionDate) |
Prove event after date |
VerifyTimeCondition(string proof, DateTime eventDate, DateTime conditionDate, string salt) |
Verify time proof |
All Prove* methods return (string Proof, string Salt). Salts are generated automatically and must be stored alongside proofs.
BulletproofsProvider -- True zero-knowledge proofs
| Method | Description |
|---|---|
ProveRange(long value, long min, long max) |
ZK range proof |
VerifyRange(byte[] proof, byte[] commitment, long min, long max) |
Verify ZK range proof |
ProveAge(DateTime birthDate, int minAge) |
ZK age proof |
VerifyAge(byte[] proof, byte[] commitment, int minAge) |
Verify ZK age proof |
ProveBalance(long balance, long requiredAmount) |
ZK balance proof |
VerifyBalance(byte[] proof, byte[] commitment, long requiredAmount) |
Verify ZK balance proof |
SerializeProof(byte[] proof, byte[] commitment) |
Serialize for storage |
DeserializeProof(string serialized) |
Deserialize proof |
Prove* methods return (byte[] proof, byte[] commitment).
Privacy SDK -- Ready-to-use privacy primitives
| Class | Method | Description |
|---|---|---|
ConfidentialTransfer |
CreateTransfer(balance, amount) |
Hide transfer amount, prove solvency |
ConfidentialTransfer |
VerifyTransfer(bundle) |
Verify without knowing the amount |
SealedBidAuction |
PlaceBid(amount) |
Commit to hidden bid with range proof |
SealedBidAuction |
VerifyBid(bid) |
Verify bid is in valid range |
SealedBidAuction |
RevealBid(bid, opening) |
Reveal and verify bid after auction |
SealedBidAuction |
DetermineWinner(bids, openings) |
Pick highest valid bid |
PrivateVoting |
CastVote(voteYes) |
Commit to binary vote (0 or 1) |
PrivateVoting |
VerifyBallot(ballot) |
Verify vote is valid without seeing it |
PrivateVoting |
Tally(ballots, secrets) |
Count votes from verified openings |
CredentialProof |
ProveMinimum(value, threshold, label) |
Prove attribute >= threshold |
CredentialProof |
ProveRange(value, min, max, label) |
Prove attribute in range |
CredentialProof |
Verify(credential) |
Verify credential proof |
StellarBlockchain -- On-chain verification
| Method | Description |
|---|---|
VerifyProof(contractId, proof, salt, value) |
HMAC proof on-chain verification |
VerifyBalanceProof(contractId, proof, balance, required, salt) |
HMAC balance proof on-chain |
VerifyZkRangeProof(contractId, proof, commitment, min, max) |
Bulletproofs range proof on-chain |
VerifyZkAgeProof(contractId, proof, commitment, minAge) |
Bulletproofs age proof on-chain |
VerifyZkBalanceProof(contractId, proof, commitment, requiredAmount) |
Bulletproofs balance proof on-chain |
VerifyProofWithTransactionXdrAsync(xdr) |
Verify using pre-built XDR |
GetAccountBalance(accountId) |
Get XLM balance for an account |
var blockchain = new StellarBlockchain(
serverUrl: "https://horizon-testnet.stellar.org",
sorobanRpcUrl: "https://soroban-testnet.stellar.org",
hmacKey: hmacKey
);
// HMAC proof on-chain verification
bool result = await blockchain.VerifyBalanceProof(contractId, proof, balance, required, salt);
// Bulletproofs ZK on-chain verification
var zkp = new BulletproofsProvider();
var (zkProof, commitment) = zkp.ProveRange(42, 0, 100);
bool zkResult = await blockchain.VerifyZkRangeProof(contractId, zkProof, commitment, 0, 100);
// ZK age verification on-chain
var (ageProof, ageCmt) = zkp.ProveAge(new DateTime(1990, 5, 15), minAge: 18);
bool ageResult = await blockchain.VerifyZkAgeProof(contractId, ageProof, ageCmt, 18);
// ZK balance verification on-chain
var (balProof, balCmt) = zkp.ProveBalance(10000, 5000);
bool balResult = await blockchain.VerifyZkBalanceProof(contractId, balProof, balCmt, 5000);SorobanTransactionBuilder -- Manual transaction construction
var builder = new SorobanTransactionBuilder(Network.Test());
// HMAC proof verification
string xdr = builder.BuildVerifyProofTransaction(contractId, proof, data, salt, hmacKey);
// ZK proof verification
string zkXdr = builder.BuildVerifyZkRangeProofTransaction(contractId, proof, commitment, min, max);
string ageXdr = builder.BuildVerifyZkAgeProofTransaction(contractId, proof, commitment, minAge);
string balXdr = builder.BuildVerifyZkBalanceProofTransaction(contractId, proof, commitment, required);Soroban contract
The Rust smart contract (contracts/stellar/contracts/proof-balance/) exposes the following functions:
| Function | Description |
|---|---|
verify_proof |
HMAC-SHA256 proof verification |
verify_balance_proof |
Balance proof with numeric comparison |
verify_batch |
Batch verification of multiple proofs |
verify_zk_range_proof |
Bulletproofs structural validation (full EC verification off-chain) |
verify_zk_age_proof |
ZK age proof structural validation |
verify_zk_balance_proof |
ZK balance proof structural validation |
See contracts/stellar/README.md for contract details and contracts/stellar/DEPLOYMENT.md for deployment instructions.
Architecture
Application
|
+-- Privacy SDK (ready-to-use solutions)
| +-- ConfidentialTransfer (hidden amounts + solvency proofs)
| +-- SealedBidAuction (commit-reveal with range proofs)
| +-- PrivateVoting (anonymous ballots + verifiable tally)
| +-- CredentialProof (threshold / range proofs for any attribute)
|
+-- Zkp (HMAC-SHA256 commitment proofs)
+-- BulletproofsProvider (real ZKP)
| +-- RangeProof (Bulletproofs prover/verifier)
| +-- PedersenCommitment (v*G + r*H on secp256k1)
| +-- InnerProductProof (recursive halving)
| +-- Transcript (Fiat-Shamir heuristic)
| +-- Point / Scalar / FieldElement (secp256k1 math)
|
+-- StellarBlockchain
+-- SorobanTransactionBuilder --> XDR
+-- SorobanRpcClient --> Soroban RPC --> ZkpVerifier Contract
Cryptography
The Bulletproofs implementation is built from scratch in pure C#:
- Elliptic curve: secp256k1 (same curve as Bitcoin) with Jacobian coordinate arithmetic
- Commitment scheme: Pedersen commitments
C = v*G + r*Hwhere G is the standard generator and H is derived via hash-to-curve - Range proof: Bulletproofs protocol with logarithmic proof size (O(log n) inner product argument)
- Non-interactive: Fiat-Shamir heuristic via SHA-256 transcript
- No unsafe dependencies: All field/scalar/point operations implemented in managed C# using
System.Numerics.BigInteger
Security
- Key management: Use environment variables for development. Use Azure Key Vault, AWS Secrets Manager, or HashiCorp Vault in production.
- Salts: Never reuse. ZkpSharp generates cryptographically secure random salts automatically.
- Bulletproofs blinding: Each proof uses a fresh random blinding factor
r, making commitments computationally hiding. - Contract verification: Always verify deployed contract code matches source.
- On-chain limitations: Full Bulletproofs verification (secp256k1 EC math) runs off-chain via
BulletproofsProvider.VerifyRange(). The Soroban contract performs structural validation only (point prefix checks, IPA length) and emits a transcript binding hash for off-chain auditing. HMAC proofs are fully verified on-chain.
Documentation
| Document | Description |
|---|---|
| QUICKSTART.md | Step-by-step setup and usage guide |
| DEPLOYMENT.md | Soroban contract deployment |
| STELLAR_REALITY_CHECK.md | Capabilities and limitations |
| INTEGRATION_STATUS.md | Feature status and migration guide |
| CHANGELOG.md | Version history |
FAQ
Why aren't Bulletproofs fully verified on-chain?
Soroban provides native support for BLS12-381 curve operations, but the Bulletproofs protocol is defined over secp256k1. Implementing full secp256k1 point arithmetic inside a Soroban contract would exceed compute budgets and add significant complexity with no upstream support. Our contract performs structural validation (compressed point format, IPA length, range bounds) and emits a transcript binding hash as an on-chain anchor. Full cryptographic verification runs off-chain via BulletproofsProvider.VerifyRange(). This is a platform limitation, not a design shortcut.
Why implement Bulletproofs from scratch instead of using an existing library?
The .NET ecosystem has no mature, maintained Bulletproofs library. Existing options are either abandoned, incomplete, or rely on unsafe native interop. We implemented the protocol from first principles using standard, well-documented primitives: secp256k1 curve parameters (same as Bitcoin), SHA-256 for Fiat-Shamir, and System.Numerics.BigInteger for field arithmetic. The implementation is covered by 44 cryptographic tests including soundness checks (tampered proofs, out-of-range values, serialization round-trips).
Why Bulletproofs and not Groth16 / zkSNARKs?
Bulletproofs are ideal for range proofs: they require no trusted setup ceremony, produce compact proofs (~690 bytes for a 64-bit range), and rely on standard discrete-log assumptions. zkSNARKs (Groth16) require a per-circuit trusted setup and are better suited for general-purpose computation proofs. Groth16 support is on the roadmap for future releases.
Is this production-ready?
The cryptographic core is functionally complete and covered by tests, but it has not undergone an independent security audit. For testnet deployments, PoCs, and non-financial applications, the library is ready to use. For production systems handling real value, we recommend a third-party cryptographic audit before deployment. The HMAC-based proof system (which does not involve custom cryptography) is suitable for production use as-is.
How does this compare to HMAC-based proofs?
| HMAC proofs | Bulletproofs | |
|---|---|---|
| Zero-knowledge | No (commitment scheme) | Yes (mathematically proven) |
| Verifier learns | Nothing if key is secret | Nothing about the secret value |
| Security basis | HMAC key secrecy | Discrete logarithm hardness |
| Proof size | 32 bytes | ~690 bytes |
| Speed | < 1 ms | ~200-500 ms (prove), ~50 ms (verify) |
| On-chain verification | Full (HMAC recomputation) | Structural only (full EC off-chain) |
| Best for | Fast checks, internal systems | Regulatory compliance, trustless scenarios |
Contributing
- Fork the repository
- Create a branch (
git checkout -b feature/your-feature) - Run tests (
dotnet test) - Submit a pull request
License
MIT License. See LICENSE for details.