| Crates.io | anonymous-credit-tokens |
| lib.rs | anonymous-credit-tokens |
| version | 0.2.1 |
| created_at | 2025-05-03 06:52:33.119996+00 |
| updated_at | 2025-05-06 00:17:45.246805+00 |
| description | An implementation of Anonymous Credit Tokens, an online variant of e-cash |
| homepage | |
| repository | https://github.com/SamuelSchlesinger/anonymous-credit-tokens |
| max_upload_size | |
| id | 1658593 |
| size | 380,887 |
A Rust implementation of an Anonymous Credit Scheme (ACS) that enables privacy-preserving payment systems for web applications and services.
This cryptography is experimental and unaudited. Do not use in production environments without thorough security review.
This library implements the Anonymous Credit Scheme designed by Jonathan Katz and Samuel Schlesinger (see design document). The system allows:
The implementation uses BBS signatures and zero-knowledge proofs to ensure both security and privacy, making it suitable for integration into web services and distributed systems.
┌──────────┐ ┌──────────────┐ ┌─────────────┐
│ Client │ │ Service │ │ Database │
│ App │◄────┤ Backend │◄────┤ (Nullifier │
│ │ │ (Issuer) │ │ Storage) │
└──────────┘ └──────────────┘ └─────────────┘
The issuer must securely generate and store a keypair:
use anonymous_credit_tokens::PrivateKey;
use rand_core::OsRng;
// Generate a keypair on service startup
let private_key = PrivateKey::random(OsRng);
let public_key = private_key.public();
// The public_key should be shared with clients
// The private_key should be securely stored
Implement a database to track used nullifiers:
use curve25519_dalek::Scalar;
// Example interface for a nullifier database
trait NullifierStore {
fn is_used(&self, nullifier: &Scalar) -> bool;
fn mark_used(&mut self, nullifier: Scalar);
}
// Example implementation using a concurrent HashMap
struct InMemoryNullifierStore {
used_nullifiers: Arc<RwLock<HashSet<Scalar>>>,
}
A typical service implementation would include these endpoints:
use anonymous_credit_tokens::PrivateKey;
use rand_core::OsRng;
// Generate a keypair for your service
let private_key = PrivateKey::random(OsRng);
let public_key = private_key.public();
use anonymous_credit_tokens::{u128_to_scalar, scalar_to_u128};
// Convert u128 to Scalar for credit amounts
let credit_amount_u128 = 500u128;
let credit_amount_scalar = u128_to_scalar(credit_amount_u128);
// Use the scalar for issuing credits
// ...
// Convert back to u128 for display or other purposes
let amount_back = scalar_to_uu128(&credit_amount_scalar).unwrap();
assert_eq!(amount_back, credit_amount_u128);
// Conversion will return None if the scalar is outside u128 range
let large_scalar = // ... some large scalar
let result = scalar_to_u128(&large_scalar); // Returns None if too large
use anonymous_credit_tokens::{Params, PreIssuance, PrivateKey};
use curve25519_dalek::Scalar;
use rand_core::OsRng;
// Client-side: Prepare for issuance
let preissuance = PreIssuance::random(OsRng);
let params = Params::nothing_up_my_sleeve(b"innocence v1");
let issuance_request = preissuance.request(¶ms, OsRng);
// Server-side: Process the request (credit amount: 20)
let credit_amount = Scalar::from(20u64);
let issuance_response = private_key
.issue(¶ms, &issuance_request, credit_amount, OsRng)
.unwrap();
// Client-side: Construct the credit token
let credit_token = preissuance
.to_credit_token(¶ms, private_key.public(), &issuance_request, &issuance_response)
.unwrap();
// Client-side: Creates a spending proof (spending 10 out of 20 credits)
let charge = Scalar::from(10u64);
let (spend_proof, prerefund) = credit_token.prove_spend(¶ms, charge, OsRng);
// Server-side: Verify and process the spending proof
// IMPORTANT: Check that the nullifier hasn't been used before
let nullifier = spend_proof.nullifier();
if nullifier_store.is_used(&nullifier) {
return Err("Double-spend attempt detected");
}
nullifier_store.mark_used(nullifier);
// Server-side: Create a refund
let refund = private_key.refund(¶ms, &spend_proof, OsRng).unwrap();
// Client-side: Construct a new credit token with remaining credits
let new_credit_token = prerefund
.to_credit_token(¶ms, &spend_proof, &refund, private_key.public())
.unwrap();
use anonymous_credit_tokens::{PrivateKey, PreIssuance};
use curve25519_dalek::Scalar;
use rand_core::OsRng;
// 1. System Initialization
let params = Params::nothing_up_my_sleeve(b"innocence v1");
let private_key = PrivateKey::random(OsRng);
// 2. User Registration/Credit Issuance
// Client prepares for issuance
let preissuance = PreIssuance::random(OsRng);
let issuance_request = preissuance.request(¶ms, OsRng);
// Server issues 40 credits
let issuance_response = private_key
.issue(¶ms, &issuance_request, Scalar::from(40u64), OsRng)
.unwrap();
// Client receives the credit token
let credit_token1 = preissuance
.to_credit_token(¶ms, private_key.public(), &issuance_request, &issuance_response)
.unwrap();
// 3. First Purchase/Transaction
// Client spends 20 credits
let charge = Scalar::from(20u64);
let (spend_proof, prerefund) = credit_token1.prove_spend(¶ms, charge, OsRng);
// Server checks nullifier and processes the spending
let nullifier = spend_proof.nullifier();
if nullifier_store.is_used(&nullifier) {
return Err("Double-spend attempt detected");
}
nullifier_store.mark_used(nullifier);
// Server issues a refund
let refund = private_key.refund(¶ms, &spend_proof, OsRng).unwrap();
// Client receives a new credit token with 20 credits remaining
let credit_token2 = prerefund
.to_credit_token(¶ms, &spend_proof, &refund, private_key.public())
.unwrap();
// 4. Second Purchase/Transaction
// Client spends remaining 20 credits
let charge = Scalar::from(20u64);
let (spend_proof2, prerefund2) = credit_token2.prove_spend(¶ms, charge, OsRng);
// Server processes as before...
This implementation uses:
The project uses Criterion.rs for benchmarking the following operations:
To run the benchmarks:
cargo bench
Benchmark results will be available in the target/criterion directory as HTML reports.
Key Management:
Database Requirements:
API Endpoints:
/api/credits/issue: Process issuance requests/api/credits/spend: Process spending proofs/api/credits/public-key: Provide the issuer's public keyClient Libraries:
User Experience:
To ensure the security of your implementation:
Double-Spending Prevention:
Key Security:
Client-Side Security:
See the LICENSE file for details.
The implementation is based on the Anonymous Credit Scheme designed by Jonathan Katz and Samuel Schlesinger. For more details, see the design document.
This is not an officially supported Google product. This project is not eligible for the Google Open Source Software Vulnerability Rewards Program.