构建可投入生产的盲签名 eCash 系统(Rust)
Source: Dev.to
简介
匿名数字现金可以在不使用区块链的情况下通过 盲签名 实现,这一概念由 David Chaum 于 1983 年提出。该协议支撑了世界上第一个数字现金系统 DigiCash。
如今,Chaum 协议的完整、可投入生产的实现已经在 Rust 中可用。
资源:
GitHub Repository | Live Demo | Crates.io
协议概述
传统数字签名
Alice → Message → Bob signs → Alice receives signature
问题:Bob 能看到消息内容。
盲签名协议
Alice → Blinded Message → Bob signs → Alice unblinds → Valid signature
Bob 永远看不到原始消息。
RSA 盲签名步骤
-
盲化(客户端)
let message = hash(serial_number); let blinding_factor = random() % n; let blinded = (message * blinding_factor.pow(e)) % n; -
签名(服务器)
let blind_signature = blinded.pow(d) % n; -
去盲化(客户端)
let signature = (blind_signature * blinding_factor.inverse()) % n;
结果是对原始消息的有效 RSA 签名,而服务器从未看到该消息本身。
系统架构
┌──────────┐ ┌────────┐ ┌────────────┐
│ Client │─────▶│ Nginx │────▶ │ API Server │
│ (Wallet) │ │ :80 │ │ :8080 │
└──────────┘ └────────┘ └──────┬─────┘
│
┌────────────────┼────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌──────┐
│ Redis │ │Postgres │ │ HSM │
│ (Cache) │ │ (Audit) │ │(Keys)│
└─────────┘ └─────────┘ └──────┘
加密原语(Rust)
pub struct BlindUser {
public_key: RsaPublicKey,
}
impl BlindUser {
pub fn blind_message(&self, message: &[u8]) -> Result {
let n = self.public_key.n();
let e = self.public_key.e();
// Hash the message
let m = BigUint::from_bytes_be(&Sha256::digest(message));
// Generate blinding factor
let r = loop {
let candidate = random_biguint(n.bits());
if candidate > 1 && gcd(&candidate, n) == 1 {
break candidate;
}
};
// Blind: m' = m * r^e mod n
let r_e = r.modpow(e, n);
let blinded = (&m * &r_e) % n;
Ok((blinded, r))
}
pub fn unblind_signature(
&self,
blind_sig: &BigUint,
blinding_factor: &BigUint,
) -> Result {
let n = self.public_key.n();
let r_inv = mod_inverse(blinding_factor, n)?;
// Unblind: s = s' * r^-1 mod n
Ok((blind_sig * r_inv) % n)
}
}
双重支付防止
pub async fn redeem_token(&self, token: &Token) -> Result {
// Fast check in Redis
if self.cache.exists(&token.serial).await? {
return Err(Error::TokenAlreadySpent);
}
// Reliable check in PostgreSQL
if self.db.is_token_spent(&token.serial).await? {
return Err(Error::TokenAlreadySpent);
}
// Atomic transaction
let tx_id = Uuid::new_v4();
sqlx::query!(
"INSERT INTO redeemed_tokens (serial, tx_id, timestamp)
VALUES ($1, $2, NOW())",
token.serial,
tx_id
)
.execute(&self.db.pool)
.await?;
self.cache.set(&token.serial, tx_id.to_string()).await?;
Ok(tx_id.to_string())
}
HTTP 处理程序(Axum)
async fn withdraw(
State(state): State>,
Json(req): Json,
) -> Result> {
// Verify denomination
if !state.is_valid_denomination(req.denomination) {
return Err(ApiError::InvalidDenomination(req.denomination));
}
// Sign each blinded token
let signatures = req.blinded_tokens
.iter()
.map(|blinded| {
state.institution
.sign_blinded(&BigUint::from_str(blinded)?)
})
.collect::>>()?;
Ok(Json(WithdrawResponse {
transaction_id: Uuid::new_v4().to_string(),
blind_signatures: signatures,
expires_at: Utc::now() + Duration::days(90),
}))
}
性能基准
| 操作 | p50 延迟 | p99 延迟 | 吞吐量 |
|---|---|---|---|
| 提现 | 45 ms | 120 ms | 150 req/s |
| 兑换 | 25 ms | 80 ms | 600 req/s |
| 验证 | 5 ms | 15 ms | 2 000 req/s |
主要瓶颈是 RSA 计算(CPU 受限)。由于 API 服务器是无状态的,水平扩展非常直接。
部署
git clone https://github.com/ChronoCoders/ecash-protocol.git
cd ecash-protocol
docker-compose up -d
该技术栈包括:
- API 服务器 (
localhost:8080) - PostgreSQL 数据库
- Redis 缓存
- 带速率限制的 Nginx 反向代理
将客户端库添加到你的项目中:
[dependencies]
ecash-client = "0.1.0"
客户端使用示例
use ecash_client::Wallet;
#[tokio::main]
async fn main() -> Result {
// Initialize wallet
let mut wallet = Wallet::new(
"http://localhost:8080".to_string(),
"wallet.db".to_string(),
)?;
wallet.initialize().await?;
// Withdraw $100 in $10 denominations
let tokens = wallet.withdraw(100, 10).await?;
println!("Withdrew {} tokens", tokens.len());
// Check balance
let balance = wallet.get_balance()?;
println!("Balance: ${}", balance);
// Spend $20
let tx_id = wallet.spend(20).await?;
println!("Transaction: {}", tx_id);
Ok(())
}
安全属性
- 不可关联性 – 服务器无法将一次提现与后续的兑换关联起来。
- 不可伪造性 – 有效的代币必须使用服务器的私有 RSA‑3072 密钥(128 位安全)进行签名。
- 双重支付防止 – 在 Redis 和 PostgreSQL 中的原子检查确保代币不会被二次消费,即使在并发请求下也是如此。
生产环境考虑
- HSM 用于私钥存储(永不将密钥存放在磁盘上)。
- TLS/HTTPS 用于所有网络流量。
- 速率限制(在 Nginx 中配置)以缓解 DoS 攻击。
- 监控 验证失败和双重支付尝试。
- 密钥轮换 – 实现定期轮换(尚未包含)。
扩展策略
| 部署规模 | 近似吞吐量 |
|---|---|
| 单节点 | ~1 000 req/s |
| 3 节点集群 | ~3 000 req/s |
| 10 节点集群 | ~10 000 req/s |
当吞吐量接近 ~5 000 req/s 时,数据库会成为瓶颈;此时应添加读副本并增加连接池大小。
参考文献
- Chaum, D. (1983). 盲签名用于不可追踪的支付。
- 完整的学术白皮书(164 KB)在仓库中 – 包含安全性证明、协议规范和性能分析。
技术栈
- 语言:Rust 1.91+
- Web 框架:Axum 0.7
- 数据库:PostgreSQL 16
- 缓存:Redis 7
- 加密:
rsacrate,使用 3072 位密钥 - 部署:Docker,Kubernetes
路线图
- 实现自动密钥轮换机制
- 为单个交易添加多种面额支持
生产就绪 v1.0.0