spark_cryptography/
signing.rs

1use std::collections::BTreeMap;
2use std::collections::BTreeSet;
3use std::collections::HashMap;
4
5use frost_core::round1::Nonce;
6use frost_core::round1::NonceCommitment;
7use frost_secp256k1_tr::keys::EvenY;
8use frost_secp256k1_tr::keys::KeyPackage as FrostKeyPackage;
9use frost_secp256k1_tr::keys::PublicKeyPackage;
10use frost_secp256k1_tr::keys::SigningShare;
11use frost_secp256k1_tr::keys::Tweak;
12use frost_secp256k1_tr::keys::VerifyingShare;
13use frost_secp256k1_tr::round1::SigningCommitments as FrostSigningCommitments;
14use frost_secp256k1_tr::round1::SigningNonces as FrostSigningNonces;
15use frost_secp256k1_tr::round2::SignatureShare;
16use frost_secp256k1_tr::Identifier;
17use frost_secp256k1_tr::SigningPackage;
18use frost_secp256k1_tr::VerifyingKey;
19
20use spark_protos::common::*;
21use spark_protos::frost::*;
22
23pub fn frost_nonce_from_proto(nonce: &SigningNonce) -> Result<FrostSigningNonces, String> {
24    let hiding_bytes = nonce.hiding.as_slice();
25    let binding_bytes = nonce.binding.as_slice();
26    let hiding = Nonce::deserialize(hiding_bytes).map_err(|e| e.to_string())?;
27    let binding = Nonce::deserialize(binding_bytes).map_err(|e| e.to_string())?;
28    Ok(FrostSigningNonces::from_nonces(hiding, binding))
29}
30
31pub fn frost_commitments_from_proto(
32    commitments: &SigningCommitment,
33) -> Result<FrostSigningCommitments, String> {
34    let hiding_bytes = commitments.hiding.as_slice();
35    let binding_bytes = commitments.binding.as_slice();
36    let hiding_commitment =
37        NonceCommitment::deserialize(hiding_bytes).map_err(|e| e.to_string())?;
38    let binding_commitment =
39        NonceCommitment::deserialize(binding_bytes).map_err(|e| e.to_string())?;
40    Ok(FrostSigningCommitments::new(
41        hiding_commitment,
42        binding_commitment,
43    ))
44}
45
46pub fn frost_signing_commitment_map_from_proto(
47    map: &HashMap<String, SigningCommitment>,
48) -> Result<BTreeMap<Identifier, FrostSigningCommitments>, String> {
49    map.iter()
50        .map(
51            |(k, v)| -> Result<(Identifier, FrostSigningCommitments), String> {
52                let identifier = hex_string_to_identifier(k)
53                    .map_err(|e| format!("Failed to parse identifier: {}", e))?;
54                let commitments = frost_commitments_from_proto(v)?;
55                Ok((identifier, commitments))
56            },
57        )
58        .collect::<Result<BTreeMap<_, _>, String>>()
59}
60
61pub fn verifying_key_from_bytes(bytes: Vec<u8>) -> Result<VerifyingKey, String> {
62    VerifyingKey::deserialize(bytes.as_slice()).map_err(|e| e.to_string())
63}
64
65pub fn frost_build_signin_package(
66    signing_commitments: BTreeMap<Identifier, FrostSigningCommitments>,
67    message: &[u8],
68    signing_participants_groups: Option<Vec<BTreeSet<Identifier>>>,
69    adaptor_public_key: &[u8],
70) -> SigningPackage {
71    let adaptor_public_key = VerifyingKey::deserialize(adaptor_public_key).ok();
72    SigningPackage::new_with_adaptor(
73        signing_commitments,
74        signing_participants_groups,
75        message,
76        adaptor_public_key,
77    )
78}
79
80pub fn frost_signature_shares_from_proto(
81    shares: &HashMap<String, Vec<u8>>,
82    user_identifier: Identifier,
83    user_signature_share: &Vec<u8>,
84) -> Result<BTreeMap<Identifier, SignatureShare>, String> {
85    let mut shares_map = shares
86        .iter()
87        .map(|(k, v)| -> Result<(Identifier, SignatureShare), String> {
88            let identifier = hex_string_to_identifier(k)
89                .map_err(|e| format!("Failed to parse identifier: {}", e))?;
90            let share = SignatureShare::deserialize(v).map_err(|e| e.to_string())?;
91            Ok((identifier, share))
92        })
93        .collect::<Result<BTreeMap<_, _>, String>>()?;
94
95    if user_signature_share.len() > 0 {
96        shares_map.insert(
97            user_identifier,
98            SignatureShare::deserialize(user_signature_share).map_err(|e| e.to_string())?,
99        );
100    }
101    Ok(shares_map)
102}
103
104pub fn frost_public_package_from_proto(
105    public_shares: &HashMap<String, Vec<u8>>,
106    user_identifier: Identifier,
107    user_public_key: Vec<u8>,
108    verifying_key: VerifyingKey,
109) -> Result<PublicKeyPackage, String> {
110    let mut final_shares = public_shares
111        .iter()
112        .map(|(k, v)| -> Result<(Identifier, VerifyingShare), String> {
113            let identifier = hex_string_to_identifier(k)?;
114            let share = VerifyingShare::deserialize(v).map_err(|e| e.to_string())?;
115            Ok((identifier, share))
116        })
117        .collect::<Result<BTreeMap<_, _>, String>>()?;
118
119    if user_public_key.len() > 0 {
120        final_shares.insert(
121            user_identifier,
122            VerifyingShare::deserialize(user_public_key.as_slice()).map_err(|e| e.to_string())?,
123        );
124    }
125    tracing::info!("final_shares: {:?}", final_shares);
126    let public_key_package = PublicKeyPackage::new(final_shares, verifying_key);
127    Ok(public_key_package)
128}
129
130pub fn frost_key_package_from_proto(
131    key_package: &KeyPackage,
132    identifier_override: Option<Identifier>,
133    verifying_key: VerifyingKey,
134    role: i32,
135) -> Result<FrostKeyPackage, String> {
136    let signing_share = SigningShare::deserialize(key_package.secret_share.as_slice())
137        .map_err(|e| e.to_string())?;
138
139    let verifying_share = VerifyingShare::deserialize(
140        key_package
141            .public_shares
142            .get(&key_package.identifier)
143            .ok_or("Verifying share is not found")?
144            .as_slice()
145            .try_into()
146            .map_err(|_| "Verifying share is not 33 bytes")?,
147    )
148    .map_err(|e| e.to_string())?;
149
150    let identifier =
151        identifier_override.unwrap_or(hex_string_to_identifier(&key_package.identifier)?);
152
153    let result = FrostKeyPackage::new(
154        identifier,
155        signing_share,
156        verifying_share,
157        verifying_key,
158        key_package.min_signers as u16,
159    );
160
161    if role == 1 {
162        // For the user, we don't want to tweak the key with merkle root, but we need to make sure the key is even.
163        // Then the total verifying key will need to tweak with the merkle root.
164        let merkle_root = vec![];
165        let result_tweaked = result.clone().tweak(Some(&merkle_root));
166        let result_even_y = result.clone().into_even_y(Some(verifying_key.has_even_y()));
167        let final_result = FrostKeyPackage::new(
168            *result_even_y.identifier(),
169            *result_even_y.signing_share(),
170            *result_even_y.verifying_share(),
171            *result_tweaked.verifying_key(),
172            *result_tweaked.min_signers(),
173        );
174        Ok(final_result)
175    } else {
176        Ok(result)
177    }
178}
179
180pub fn frost_nonce(req: &FrostNonceRequest) -> Result<FrostNonceResponse, String> {
181    let mut results = Vec::new();
182
183    for key_package in req.key_packages.iter() {
184        let verifying_key = verifying_key_from_bytes(key_package.public_key.clone())
185            .map_err(|e| format!("Failed to parse verifying key: {:?}", e))?;
186        let key_package = frost_key_package_from_proto(key_package, None, verifying_key, 0)
187            .map_err(|e| format!("Failed to parse key package: {:?}", e))?;
188
189        let rng = &mut rand::thread_rng();
190        let (nonce, commitment) =
191            frost_secp256k1_tr::round1::commit(&key_package.signing_share(), rng);
192
193        let pb_nonce = SigningNonce {
194            hiding: nonce.hiding().serialize().to_vec(),
195            binding: nonce.binding().serialize().to_vec(),
196        };
197
198        let pb_commitment = SigningCommitment {
199            hiding: commitment
200                .hiding()
201                .serialize()
202                .map_err(|e| format!("Failed to serialize hiding commitment: {:?}", e))?,
203            binding: commitment
204                .binding()
205                .serialize()
206                .map_err(|e| format!("Failed to serialize binding commitment: {:?}", e))?,
207        };
208
209        results.push(SigningNonceResult {
210            nonces: Some(pb_nonce),
211            commitments: Some(pb_commitment),
212        });
213    }
214
215    Ok(FrostNonceResponse { results })
216}
217
218pub fn sign_frost(req: &SignFrostRequest) -> Result<SignFrostResponse, String> {
219    let mut results = HashMap::new();
220    for job in req.signing_jobs.iter() {
221        let mut commitments = frost_signing_commitment_map_from_proto(&job.commitments)
222            .map_err(|e| format!("Failed to parse signing commitments: {:?}", e))?;
223
224        let user_identifier =
225            Identifier::derive("user".as_bytes()).expect("Failed to derive user identifier");
226
227        let mut signing_participants_groups = Vec::new();
228        signing_participants_groups.push(commitments.keys().cloned().collect());
229
230        tracing::debug!("User commitments: {:?}", job.user_commitments);
231
232        match &job.user_commitments {
233            Some(c) => {
234                let user_commitments = frost_commitments_from_proto(c)
235                    .map_err(|e| format!("Failed to parse user commitments: {:?}", e))?;
236                commitments.insert(user_identifier, user_commitments);
237                signing_participants_groups.push(BTreeSet::from([user_identifier]));
238            }
239            None => {}
240        };
241        tracing::debug!("There are {} commitments", commitments.len());
242
243        let nonce = match &job.nonce {
244            Some(nonce) => frost_nonce_from_proto(nonce)
245                .map_err(|e| format!("Failed to parse nonce: {:?}", e))?,
246            None => return Err(format!("Nonce is required")),
247        };
248
249        let verifying_key = verifying_key_from_bytes(job.verifying_key.clone())
250            .map_err(|e| format!("Failed to parse verifying key: {:?}", e))?;
251
252        let identifier_override = match req.role {
253            0 => None,
254            1 => Some(user_identifier),
255            _ => return Err(format!("Invalid signing role")),
256        };
257
258        let key_package = match &job.key_package {
259            Some(key_package) => frost_key_package_from_proto(
260                key_package,
261                identifier_override,
262                verifying_key,
263                req.role,
264            )
265            .map_err(|e| format!("Failed to parse key package: {:?}", e))?,
266            None => return Err(format!("Key package is required")),
267        };
268
269        let signing_package = frost_build_signin_package(
270            commitments,
271            &job.message,
272            Some(signing_participants_groups),
273            &job.adaptor_public_key,
274        );
275
276        tracing::info!("Building signing package completed");
277        let tweak = vec![];
278        let signature_share = match req.role {
279            0 => frost_secp256k1_tr::round2::sign_with_tweak(
280                &signing_package,
281                &nonce,
282                &key_package,
283                Some(tweak.as_slice()),
284            )
285            .map_err(|e| format!("Failed to sign frost: {:?}", e))?,
286            _ => frost_secp256k1_tr::round2::sign(&signing_package, &nonce, &key_package)
287                .map_err(|e| format!("Failed to sign frost: {:?}", e))?,
288        };
289        tracing::info!("Signing frost completed");
290
291        results.insert(
292            job.job_id.clone(),
293            SigningResult {
294                signature_share: signature_share.serialize().to_vec(),
295            },
296        );
297    }
298
299    Ok(SignFrostResponse { results })
300}
301
302pub fn aggregate_frost(req: &AggregateFrostRequest) -> Result<AggregateFrostResponse, String> {
303    let mut commitments = frost_signing_commitment_map_from_proto(&req.commitments)
304        .map_err(|e| format!("Failed to parse signing commitments: {:?}", e))?;
305
306    let mut signing_participants_groups = Vec::new();
307    signing_participants_groups.push(commitments.keys().cloned().collect());
308
309    let user_identifier =
310        Identifier::derive("user".as_bytes()).expect("Failed to derive user identifier");
311
312    match &req.user_commitments {
313        Some(c) => {
314            let user_commitments = frost_commitments_from_proto(c)
315                .map_err(|e| format!("Failed to parse user commitments: {:?}", e))?;
316            commitments.insert(user_identifier, user_commitments);
317        }
318        None => {}
319    };
320
321    let verifying_key = verifying_key_from_bytes(req.verifying_key.clone())
322        .map_err(|e| format!("Failed to parse verifying key: {:?}", e))?;
323
324    signing_participants_groups.push(BTreeSet::from([user_identifier]));
325
326    let signing_package = frost_build_signin_package(
327        commitments,
328        &req.message,
329        Some(signing_participants_groups),
330        &req.adaptor_public_key,
331    );
332
333    let signature_shares = frost_signature_shares_from_proto(
334        &req.signature_shares,
335        user_identifier,
336        &req.user_signature_share,
337    )
338    .map_err(|e| format!("Failed to parse signature shares: {:?}", e))?;
339
340    let public_package = frost_public_package_from_proto(
341        &req.public_shares,
342        user_identifier,
343        req.user_public_key.clone(),
344        verifying_key,
345    )
346    .map_err(|e| format!("Failed to parse public package: {:?}", e))?;
347
348    let tweak = vec![];
349
350    tracing::info!("signing_package: {:?}", signing_package);
351    tracing::info!("signature_shares: {:?}", signature_shares);
352    tracing::info!("public_package: {:?}", public_package);
353
354    let signature = frost_secp256k1_tr::aggregate_with_tweak(
355        &signing_package,
356        &signature_shares,
357        &public_package,
358        Some(&tweak),
359    )
360    .map_err(|e| format!("Failed to aggregate frost: {:?}", e))?;
361
362    Ok(AggregateFrostResponse {
363        signature: signature
364            .serialize()
365            .map_err(|e| format!("Failed to serialize signature: {:?}", e))?,
366    })
367}
368
369pub fn validate_signature_share(req: &ValidateSignatureShareRequest) -> Result<(), String> {
370    let identifier = match req.role {
371        0 => hex_string_to_identifier(&req.identifier)
372            .map_err(|e| format!("Failed to parse identifier: {:?}", e))?,
373        1 => Identifier::derive("user".as_bytes()).expect("Failed to derive user identifier"),
374        _ => return Err(format!("Invalid signing role")),
375    };
376
377    let signature_share = SignatureShare::deserialize(req.signature_share.as_slice())
378        .map_err(|e| format!("Failed to parse signature share: {:?}", e))?;
379    let verifying_key = verifying_key_from_bytes(req.verifying_key.clone())
380        .map_err(|e| format!("Failed to parse verifying key: {:?}", e))?;
381
382    let mut commitments = frost_signing_commitment_map_from_proto(&req.commitments)
383        .map_err(|e| format!("Failed to parse signing commitments: {:?}", e))?;
384
385    let user_identifier =
386        Identifier::derive("user".as_bytes()).expect("Failed to derive user identifier");
387
388    let mut signing_participants_groups = Vec::new();
389    signing_participants_groups.push(commitments.keys().cloned().collect());
390
391    if let Some(c) = &req.user_commitments {
392        let user_commitments = frost_commitments_from_proto(c)
393            .map_err(|e| format!("Failed to parse user commitments: {:?}", e))?;
394        commitments.insert(user_identifier, user_commitments);
395        signing_participants_groups.push(BTreeSet::from([user_identifier]));
396    }
397
398    let public_share = VerifyingShare::deserialize(req.public_share.as_slice())
399        .map_err(|e| format!("Failed to parse public share: {:?}", e))?;
400
401    let adaptor_key: Vec<u8> = vec![];
402
403    let signing_package = frost_build_signin_package(
404        commitments,
405        &req.message,
406        Some(signing_participants_groups),
407        &adaptor_key,
408    );
409
410    let dummy_signing_share = SigningShare::deserialize(&[0; 32])
411        .map_err(|e| format!("Failed to parse dummy signing share: {:?}", e))?;
412
413    let result = FrostKeyPackage::new(
414        identifier,
415        dummy_signing_share,
416        public_share,
417        verifying_key,
418        2,
419    );
420    let merkle_root = vec![];
421    let result_tweaked = result.clone().tweak(Some(&merkle_root));
422    let result_even_y = result.clone().into_even_y(Some(verifying_key.has_even_y()));
423
424    let verifying_share = match req.role {
425        0 => result_tweaked.verifying_share(),
426        1 => result_even_y.verifying_share(),
427        _ => return Err(format!("Invalid signing role")),
428    };
429
430    let verify_identifier = match req.role {
431        0 => identifier,
432        1 => user_identifier,
433        _ => return Err(format!("Invalid signing role")),
434    };
435
436    frost_secp256k1_tr::verify_signature_share(
437        verify_identifier,
438        &verifying_share,
439        &signature_share,
440        &signing_package,
441        &result_tweaked.verifying_key(),
442    )
443    .map_err(|e| format!("Failed to verify signature share: {:?}", e))?;
444
445    tracing::info!("Signature share is valid");
446
447    Ok(())
448}
449
450/// Convert a hex string to an identifier.
451pub fn hex_string_to_identifier(identifier: &str) -> Result<Identifier, String> {
452    let id_bytes: [u8; 32] = hex::decode(identifier)
453        .map_err(|e| format!("Invalid hex: {:?}", e))?
454        .try_into()
455        .map_err(|e| format!("Identifier is not 32 bytes: {:?}", e))?;
456    Identifier::deserialize(&id_bytes)
457        .map_err(|e| format!("Failed to deserialize identifier: {:?}", e))
458}