1use blake3;
16use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
17use rand::Rng;
18use thiserror::Error;
19use zeroize::Zeroize;
20
21#[derive(Debug, Error)]
23pub enum BlindError {
24 #[error("Invalid commitment")]
25 InvalidCommitment,
26 #[error("Invalid token signature")]
27 InvalidSignature,
28 #[error("Signature verification failed")]
29 VerificationFailed,
30 #[error("Invalid public key")]
31 InvalidPublicKey,
32 #[error("Token already spent")]
33 TokenAlreadySpent,
34}
35
36pub type BlindResult<T> = Result<T, BlindError>;
37
38#[derive(Clone, Debug)]
40pub struct UnlinkableToken {
41 pub serial: [u8; 32],
43 pub value: u64,
45 pub expiry: u64,
47}
48
49#[derive(Clone, Zeroize)]
51#[zeroize(drop)]
52pub struct BlindingFactor {
53 factor: [u8; 32],
54}
55
56#[derive(Clone, Debug)]
58pub struct TokenCommitment {
59 commitment: [u8; 32],
60}
61
62#[derive(Clone, Debug)]
64pub struct SignedCommitment {
65 commitment: [u8; 32],
66 signature: [u8; 64],
67}
68
69#[derive(Clone, Debug)]
71pub struct RedeemableToken {
72 pub token: UnlinkableToken,
73 pub blinding_factor: [u8; 32],
74 pub signature: [u8; 64],
75}
76
77impl UnlinkableToken {
78 pub fn new(value: u64, expiry: u64) -> Self {
80 let mut rng = rand::thread_rng();
81 let mut serial = [0u8; 32];
82 rng.fill(&mut serial);
83 Self {
84 serial,
85 value,
86 expiry,
87 }
88 }
89
90 pub fn with_serial(serial: [u8; 32], value: u64, expiry: u64) -> Self {
92 Self {
93 serial,
94 value,
95 expiry,
96 }
97 }
98
99 pub fn to_bytes(&self) -> Vec<u8> {
101 let mut bytes = Vec::new();
102 bytes.extend_from_slice(&self.serial);
103 bytes.extend_from_slice(&self.value.to_le_bytes());
104 bytes.extend_from_slice(&self.expiry.to_le_bytes());
105 bytes
106 }
107}
108
109impl BlindingFactor {
110 pub fn generate() -> Self {
112 let mut rng = rand::thread_rng();
113 let mut factor = [0u8; 32];
114 rng.fill(&mut factor);
115 Self { factor }
116 }
117
118 pub fn from_bytes(bytes: [u8; 32]) -> Self {
120 Self { factor: bytes }
121 }
122
123 pub fn to_bytes(&self) -> [u8; 32] {
125 self.factor
126 }
127
128 pub fn commit(&self, token: &UnlinkableToken) -> TokenCommitment {
130 let mut hasher = blake3::Hasher::new();
131 hasher.update(b"TOKEN_COMMITMENT:");
132 hasher.update(&token.to_bytes());
133 hasher.update(&self.factor);
134 TokenCommitment {
135 commitment: *hasher.finalize().as_bytes(),
136 }
137 }
138
139 pub fn verify_commitment(&self, token: &UnlinkableToken, commitment: &TokenCommitment) -> bool {
141 let recomputed = self.commit(token);
142 recomputed.commitment == commitment.commitment
143 }
144}
145
146impl TokenCommitment {
147 pub fn as_bytes(&self) -> &[u8; 32] {
149 &self.commitment
150 }
151
152 pub fn from_bytes(bytes: [u8; 32]) -> Self {
154 Self { commitment: bytes }
155 }
156}
157
158pub struct BlindSigner {
160 signing_key: SigningKey,
161}
162
163impl BlindSigner {
164 pub fn generate() -> Self {
166 let mut rng = rand::thread_rng();
167 Self {
168 signing_key: SigningKey::generate(&mut rng),
169 }
170 }
171
172 pub fn from_signing_key(signing_key: SigningKey) -> Self {
174 Self { signing_key }
175 }
176
177 pub fn from_bytes(bytes: &[u8; 32]) -> Self {
179 Self {
180 signing_key: SigningKey::from_bytes(bytes),
181 }
182 }
183
184 pub fn public_key(&self) -> BlindPublicKey {
186 BlindPublicKey {
187 verifying_key: self.signing_key.verifying_key(),
188 }
189 }
190
191 pub fn sign_commitment(&self, commitment: &TokenCommitment) -> SignedCommitment {
193 let signature = self.signing_key.sign(&commitment.commitment);
194 SignedCommitment {
195 commitment: commitment.commitment,
196 signature: signature.to_bytes(),
197 }
198 }
199
200 pub fn to_bytes(&self) -> [u8; 32] {
202 self.signing_key.to_bytes()
203 }
204}
205
206#[derive(Clone, Debug)]
208pub struct BlindPublicKey {
209 verifying_key: VerifyingKey,
210}
211
212impl BlindPublicKey {
213 pub fn from_bytes(bytes: &[u8; 32]) -> BlindResult<Self> {
215 let verifying_key =
216 VerifyingKey::from_bytes(bytes).map_err(|_| BlindError::InvalidPublicKey)?;
217 Ok(Self { verifying_key })
218 }
219
220 pub fn to_bytes(&self) -> [u8; 32] {
222 self.verifying_key.to_bytes()
223 }
224
225 pub fn verify_token(&self, redeemable: &RedeemableToken) -> BlindResult<()> {
231 let blinding = BlindingFactor::from_bytes(redeemable.blinding_factor);
233 let commitment = blinding.commit(&redeemable.token);
234
235 let signature = Signature::from_bytes(&redeemable.signature);
237 self.verifying_key
238 .verify(&commitment.commitment, &signature)
239 .map_err(|_| BlindError::VerificationFailed)?;
240
241 Ok(())
242 }
243
244 pub fn verify_commitment(&self, signed: &SignedCommitment) -> BlindResult<()> {
246 let signature = Signature::from_bytes(&signed.signature);
247 self.verifying_key
248 .verify(&signed.commitment, &signature)
249 .map_err(|_| BlindError::VerificationFailed)?;
250 Ok(())
251 }
252}
253
254pub struct BlindSignatureProtocol;
256
257impl BlindSignatureProtocol {
258 pub fn create_token(
262 value: u64,
263 expiry: u64,
264 ) -> (TokenCommitment, UnlinkableToken, BlindingFactor) {
265 let token = UnlinkableToken::new(value, expiry);
266 let blinding = BlindingFactor::generate();
267 let commitment = blinding.commit(&token);
268 (commitment, token, blinding)
269 }
270
271 pub fn issue_token(issuer: &BlindSigner, commitment: &TokenCommitment) -> SignedCommitment {
273 issuer.sign_commitment(commitment)
274 }
275
276 pub fn prepare_redemption(
278 token: UnlinkableToken,
279 blinding: BlindingFactor,
280 signed: SignedCommitment,
281 ) -> RedeemableToken {
282 RedeemableToken {
283 token,
284 blinding_factor: blinding.to_bytes(),
285 signature: signed.signature,
286 }
287 }
288
289 pub fn verify_and_redeem(
291 public_key: &BlindPublicKey,
292 redeemable: &RedeemableToken,
293 current_time: u64,
294 ) -> BlindResult<()> {
295 if current_time > redeemable.token.expiry {
297 return Err(BlindError::VerificationFailed);
298 }
299
300 public_key.verify_token(redeemable)?;
302
303 Ok(())
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310
311 #[test]
312 fn test_unlinkable_token_flow() {
313 let issuer = BlindSigner::generate();
314 let public_key = issuer.public_key();
315
316 let (commitment, token, blinding) = BlindSignatureProtocol::create_token(100, u64::MAX);
318
319 let signed = BlindSignatureProtocol::issue_token(&issuer, &commitment);
321
322 let redeemable = BlindSignatureProtocol::prepare_redemption(token, blinding, signed);
324
325 BlindSignatureProtocol::verify_and_redeem(&public_key, &redeemable, 0).unwrap();
327 }
328
329 #[test]
330 fn test_commitment_verification() {
331 let token = UnlinkableToken::new(50, u64::MAX);
332 let blinding = BlindingFactor::generate();
333 let commitment = blinding.commit(&token);
334
335 assert!(blinding.verify_commitment(&token, &commitment));
337
338 let wrong_blinding = BlindingFactor::generate();
340 assert!(!wrong_blinding.verify_commitment(&token, &commitment));
341 }
342
343 #[test]
344 fn test_different_tokens() {
345 let issuer = BlindSigner::generate();
346 let public_key = issuer.public_key();
347
348 let (comm1, tok1, blind1) = BlindSignatureProtocol::create_token(100, u64::MAX);
350 let (comm2, tok2, blind2) = BlindSignatureProtocol::create_token(200, u64::MAX);
351
352 assert_ne!(tok1.serial, tok2.serial);
353 assert_ne!(comm1.as_bytes(), comm2.as_bytes());
354
355 let signed1 = issuer.sign_commitment(&comm1);
357 let signed2 = issuer.sign_commitment(&comm2);
358
359 let redeem1 = BlindSignatureProtocol::prepare_redemption(tok1, blind1, signed1);
361 let redeem2 = BlindSignatureProtocol::prepare_redemption(tok2, blind2, signed2);
362
363 public_key.verify_token(&redeem1).unwrap();
364 public_key.verify_token(&redeem2).unwrap();
365 }
366
367 #[test]
368 fn test_wrong_blinding() {
369 let issuer = BlindSigner::generate();
370 let public_key = issuer.public_key();
371
372 let (commitment, token, _correct_blinding) =
373 BlindSignatureProtocol::create_token(100, u64::MAX);
374 let signed = issuer.sign_commitment(&commitment);
375
376 let wrong_blinding = BlindingFactor::generate();
378 let wrong_redeemable = RedeemableToken {
379 token,
380 blinding_factor: wrong_blinding.to_bytes(),
381 signature: signed.signature,
382 };
383
384 assert!(public_key.verify_token(&wrong_redeemable).is_err());
386 }
387
388 #[test]
389 fn test_expired_token() {
390 let issuer = BlindSigner::generate();
391 let public_key = issuer.public_key();
392
393 let (commitment, token, blinding) = BlindSignatureProtocol::create_token(100, 1000);
395 let signed = issuer.sign_commitment(&commitment);
396 let redeemable = BlindSignatureProtocol::prepare_redemption(token, blinding, signed);
397
398 BlindSignatureProtocol::verify_and_redeem(&public_key, &redeemable, 999).unwrap();
400
401 assert!(BlindSignatureProtocol::verify_and_redeem(&public_key, &redeemable, 1001).is_err());
403 }
404
405 #[test]
406 fn test_unlinkability() {
407 let issuer = BlindSigner::generate();
408
409 let (comm1, tok1, _) = BlindSignatureProtocol::create_token(100, u64::MAX);
411 let (comm2, tok2, _) = BlindSignatureProtocol::create_token(100, u64::MAX);
412
413 assert_ne!(comm1.as_bytes(), comm2.as_bytes());
415 assert_ne!(tok1.serial, tok2.serial);
416
417 let sig1 = issuer.sign_commitment(&comm1);
419 let sig2 = issuer.sign_commitment(&comm2);
420 assert_ne!(sig1.signature, sig2.signature);
421 }
422
423 #[test]
424 fn test_serialization() {
425 let token = UnlinkableToken::new(123, 456);
426 let bytes = token.to_bytes();
427 assert_eq!(bytes.len(), 32 + 8 + 8);
428
429 assert_eq!(&bytes[..32], &token.serial);
431 }
432
433 #[test]
434 fn test_key_serialization() {
435 let issuer = BlindSigner::generate();
436 let public_key = issuer.public_key();
437
438 let issuer_bytes = issuer.to_bytes();
440 let issuer2 = BlindSigner::from_bytes(&issuer_bytes);
441
442 let pk_bytes = public_key.to_bytes();
443 let pk2 = BlindPublicKey::from_bytes(&pk_bytes).unwrap();
444
445 let (commitment, token, blinding) = BlindSignatureProtocol::create_token(100, u64::MAX);
447 let signed = issuer2.sign_commitment(&commitment);
448 let redeemable = BlindSignatureProtocol::prepare_redemption(token, blinding, signed);
449
450 pk2.verify_token(&redeemable).unwrap();
451 }
452
453 #[test]
454 fn test_blinding_factor_zeroize() {
455 let factor = BlindingFactor::generate();
456 let _bytes = factor.to_bytes();
457
458 drop(factor);
460 }
461}