1mod types;
2
3use alloy::primitives::{Bytes, FixedBytes};
4use blind_rsa_signatures::reexports::rsa::BigUint;
5use blind_rsa_signatures::reexports::rsa::PublicKeyParts;
6use blind_rsa_signatures::reexports::rsa::RsaPublicKey as BlindRsaPublicKey;
7use blind_rsa_signatures::{
8 BlindSignature, MessageRandomizer, Options, PublicKey, Secret, Signature,
9};
10use rand::{CryptoRng, RngCore};
11use sha2::{Digest, Sha256};
12use std::error::Error;
13pub use types::{BlindedSignature, SignedTicket, UnsignedTicket};
14
15#[derive(Debug)]
17pub enum VerifierError {
18 BlindingFailed(String),
19 FinalizationFailed(String),
20 VerificationFailed(String),
21 LengthMismatch,
22 IdMismatch,
23}
24
25impl std::fmt::Display for VerifierError {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 match self {
28 VerifierError::BlindingFailed(e) => write!(f, "Blinding failed: {}", e),
29 VerifierError::FinalizationFailed(e) => write!(f, "Finalization failed: {}", e),
30 VerifierError::VerificationFailed(e) => write!(f, "Verification failed: {}", e),
31 VerifierError::LengthMismatch => write!(f, "Mismatched lengths between inputs"),
32 VerifierError::IdMismatch => {
33 write!(f, "ID mismatch between ticket and blind signature")
34 }
35 }
36 }
37}
38
39impl Error for VerifierError {}
40
41pub struct TicketsVerifier {
43 pub public_key: PublicKey,
44}
45
46impl TicketsVerifier {
47 pub fn new(public_key: PublicKey) -> Self {
49 Self { public_key }
50 }
51
52 pub fn get_options(&self) -> Options {
53 Options::default()
54 }
55
56 pub fn from_hex_string(hex_key: &str) -> Result<Self, VerifierError> {
58 let hex = hex_key.trim_start_matches("0x");
60
61 let exponent_hex = &hex[0..6]; let modulus_hex = &hex[8..];
64
65 let exponent_bytes = hex::decode(exponent_hex).expect("Invalid exponent hex");
67 let modulus_bytes = hex::decode(modulus_hex).expect("Invalid modulus hex");
68
69 let exponent = BigUint::from_bytes_be(&exponent_bytes);
71 let modulus = BigUint::from_bytes_be(&modulus_bytes);
72
73 let blind_key = BlindRsaPublicKey::new(modulus, exponent).expect("Failed to convert key");
75
76 let public_key = PublicKey(blind_key);
77 Ok(Self { public_key })
78 }
79
80 pub fn to_hex_string(&self) -> String {
81 let modulus = self.public_key.n().to_bytes_be();
83 let exponent = self.public_key.e().to_bytes_be();
84
85 let modulus_hex = hex::encode(modulus);
87 let exponent_hex = hex::encode(exponent);
88
89 format!("0x{}00{}", exponent_hex, modulus_hex)
91 }
92
93 pub fn new_blind_tickets<R: RngCore + CryptoRng>(
95 &self,
96 rng: &mut R,
97 count: usize,
98 ) -> Result<Vec<UnsignedTicket>, VerifierError> {
99 let mut tickets = Vec::new();
100 let options = Options::default();
101
102 for _ in 0..count {
103 let mut msg = vec![0u8; 32];
105 rng.fill_bytes(&mut msg);
106
107 let blinding_result = self
109 .public_key
110 .blind(rng, &msg, true, &options)
111 .map_err(|e| VerifierError::BlindingFailed(format!("{:?}", e)))?;
112
113 let mut hasher = Sha256::new();
115 hasher.update(&blinding_result.blind_msg);
116 let id = FixedBytes::from_slice(&hasher.finalize());
117
118 let msg_randomizer = match blinding_result.msg_randomizer {
119 Some(r) => FixedBytes::from_slice(r.as_ref()),
120 None => FixedBytes::from_slice(&[0; 32]),
121 };
122
123 tickets.push(UnsignedTicket {
124 msg: Bytes::copy_from_slice(&msg),
125 blind_msg: Bytes::copy_from_slice(&blinding_result.blind_msg),
126 msg_randomizer: msg_randomizer,
127 id: id,
128 secret: Bytes::copy_from_slice(&blinding_result.secret),
129 });
130 }
131
132 Ok(tickets)
133 }
134
135 pub fn finalize_tickets(
137 &self,
138 tickets: Vec<UnsignedTicket>,
139 blind_signatures: Vec<BlindedSignature>,
140 ) -> Result<Vec<SignedTicket>, VerifierError> {
141 if tickets.len() != blind_signatures.len() {
142 return Err(VerifierError::LengthMismatch);
143 }
144
145 let options = Options::default();
146 let mut signed_tickets = Vec::new();
147
148 for (ticket, blind_sig) in tickets.into_iter().zip(blind_signatures.into_iter()) {
149 if ticket.id != blind_sig.id {
150 return Err(VerifierError::IdMismatch);
151 }
152
153 let sig = BlindSignature(blind_sig.blind_sig.to_vec());
154 let secret = Secret(ticket.secret.to_vec());
155 let msg = ticket.msg.to_vec();
156 let msg_randomizer = {
157 let raw = ticket.msg_randomizer.to_vec();
158 if raw.iter().all(|&b| b == 0) {
159 None
160 } else {
161 let mut arr = [0u8; 32];
162 arr.copy_from_slice(&raw);
163 Some(MessageRandomizer(arr))
164 }
165 };
166
167 let finalized_sig = self
168 .public_key
169 .finalize(&sig, &secret, msg_randomizer, &msg, &options)
170 .map_err(|e| VerifierError::FinalizationFailed(format!("{:?}", e)))?;
171
172 signed_tickets.push(SignedTicket {
173 msg: ticket.msg,
174 msg_randomizer: ticket.msg_randomizer,
175 finalized_sig: Bytes::copy_from_slice(&finalized_sig),
176 });
177 }
178
179 Ok(signed_tickets)
180 }
181
182 pub fn verify_signed_ticket(
183 &self,
184 signed_ticket: &SignedTicket,
185 options: &Options,
186 ) -> Result<(), VerifierError> {
187 let sig = Signature(signed_ticket.finalized_sig.to_vec());
188 let msg = signed_ticket.msg.to_vec();
189 let msg_randomizer = {
190 let raw = signed_ticket.msg_randomizer.to_vec();
191 if raw.iter().all(|&b| b == 0) {
192 None
193 } else {
194 let mut arr = [0u8; 32];
195 arr.copy_from_slice(&raw);
196 Some(MessageRandomizer(arr))
197 }
198 };
199
200 self.public_key
201 .verify(&sig, msg_randomizer, &msg, options)
202 .map_err(|_| VerifierError::VerificationFailed("Invalid signature".to_string()))?;
203
204 Ok(())
205 }
206
207 pub fn verify_signed_tickets(
209 &self,
210 signed_tickets: Vec<SignedTicket>,
211 ) -> Result<(), VerifierError> {
212 let options = Options::default();
213 for ticket in signed_tickets {
214 self.verify_signed_ticket(&ticket, &options)?;
215 }
216
217 Ok(())
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224 use blind_rsa_signatures::KeyPair;
225 use rand::thread_rng;
226
227 #[test]
228 fn test_verifier_from_hex_string() {
229 let mut rng = thread_rng();
231 let kp = KeyPair::generate(&mut rng, 2048).unwrap();
232 let pub_key = kp.pk;
233
234 let modulus = pub_key.n().to_bytes_be();
236 let exponent = pub_key.e().to_bytes_be();
237 let hex_key = format!("0x{}00{}", hex::encode(exponent), hex::encode(modulus));
238
239 let coordinator_pubkey = TicketsVerifier::from_hex_string(&hex_key)
241 .expect("Failed to parse public key from hex");
242
243 assert_eq!(coordinator_pubkey.public_key.n(), pub_key.n());
245 assert_eq!(coordinator_pubkey.public_key.e(), pub_key.e());
246 }
247
248 #[test]
249 fn test_new_blind_tickets() {
250 let mut rng = thread_rng();
252 let kp = KeyPair::generate(&mut rng, 2048).unwrap();
253 let coordinator = TicketsVerifier::new(kp.pk);
254
255 let tickets = coordinator
257 .new_blind_tickets(&mut rng, 5)
258 .expect("Failed to generate blind tickets");
259
260 assert_eq!(tickets.len(), 5);
262 for ticket in tickets {
263 assert!(!ticket.msg.is_empty());
264 assert!(!ticket.blind_msg.is_empty());
265 assert!(!ticket.msg_randomizer.is_zero());
266 assert!(!ticket.id.is_zero());
267 assert!(!ticket.secret.is_empty());
268 }
269 }
270
271 #[test]
272 fn test_finalize_tickets() {
273 let mut rng = thread_rng();
275 let kp = KeyPair::generate(&mut rng, 2048).unwrap();
276 let priv_key = kp.sk;
277 let coordinator = TicketsVerifier::new(kp.pk);
278
279 let tickets = coordinator
281 .new_blind_tickets(&mut rng, 3)
282 .expect("Failed to generate blind tickets");
283
284 let mut blind_signatures = Vec::new();
286 for ticket in &tickets {
287 let blind_sig = priv_key
288 .blind_sign(&mut rng, ticket.blind_msg.as_ref(), &Options::default())
289 .expect("Failed to sign blind message");
290
291 blind_signatures.push(BlindedSignature {
292 blind_sig: Bytes::copy_from_slice(&blind_sig),
293 id: ticket.id.clone(),
294 });
295 }
296
297 let signed_tickets = coordinator
299 .finalize_tickets(tickets, blind_signatures)
300 .expect("Failed to finalize tickets");
301
302 assert_eq!(signed_tickets.len(), 3);
304 for signed_ticket in &signed_tickets {
305 assert!(!signed_ticket.msg.is_empty());
306 assert!(!signed_ticket.msg_randomizer.is_zero());
307 assert!(!signed_ticket.finalized_sig.is_empty());
308 }
309 }
310
311 #[test]
312 fn test_verify_signed_tickets() {
313 let mut rng = thread_rng();
315 let kp = KeyPair::generate(&mut rng, 2048).unwrap();
316 let priv_key = kp.sk;
317 let coordinator = TicketsVerifier::new(kp.pk);
318
319 let tickets = coordinator
321 .new_blind_tickets(&mut rng, 3)
322 .expect("Failed to generate blind tickets");
323
324 let mut blind_signatures = Vec::new();
326 for ticket in &tickets {
327 let blind_sig = priv_key
328 .blind_sign(&mut rng, ticket.blind_msg.as_ref(), &Options::default())
329 .expect("Failed to sign blind message");
330
331 blind_signatures.push(BlindedSignature {
332 blind_sig: Bytes::copy_from_slice(&blind_sig),
333 id: ticket.id.clone(),
334 });
335 }
336
337 let signed_tickets = coordinator
339 .finalize_tickets(tickets, blind_signatures)
340 .expect("Failed to finalize tickets");
341
342 coordinator
344 .verify_signed_tickets(signed_tickets)
345 .expect("Verification failed");
346 }
347}