Skip to main content

yip_ca/threshold/
mod.rs

1use frost::keys::{IdentifierList, KeyPackage, PublicKeyPackage};
2use frost::round1::{SigningCommitments, SigningNonces};
3use frost::round2::SignatureShare;
4use frost::{Identifier, SigningPackage};
5use frost_ed25519 as frost;
6pub mod dkg;
7pub mod refresh;
8
9use std::collections::BTreeMap;
10use thiserror::Error;
11
12#[derive(Debug, Error)]
13pub enum ThresholdError {
14    #[error("FROST error: {0}")]
15    Frost(#[from] frost::Error),
16    #[error("Invalid parameters: {0}")]
17    InvalidParams(&'static str),
18    #[error("Serialization error: {0}")]
19    Serialization(String),
20}
21
22/// Generates a threshold key set using a trusted dealer.
23pub fn dealer_generate(
24    threshold: u16,
25    max_signers: u16,
26) -> Result<(PublicKeyPackage, BTreeMap<Identifier, KeyPackage>), ThresholdError> {
27    if threshold == 0 || threshold > max_signers {
28        return Err(ThresholdError::InvalidParams(
29            "threshold must be >= 1 and <= max_signers",
30        ));
31    }
32
33    let rng = rand::rngs::OsRng;
34    let (shares, pubkey_package) =
35        frost::keys::generate_with_dealer(max_signers, threshold, IdentifierList::Default, rng)?;
36
37    let mut key_packages = BTreeMap::new();
38    for (id, secret_share) in shares {
39        let key_package = KeyPackage::try_from(secret_share)?;
40        key_packages.insert(id, key_package);
41    }
42
43    Ok((pubkey_package, key_packages))
44}
45
46/// Executes threshold signing over a canonical message buffer using the provided signers.
47/// Returns a standard 64-byte Ed25519 signature.
48pub fn threshold_sign_cert(
49    tbs: &[u8],
50    commitments_map: BTreeMap<Identifier, SigningCommitments>,
51    signers: Vec<(Identifier, &KeyPackage, SigningNonces)>,
52    pubkey_package: &PublicKeyPackage,
53) -> Result<[u8; 64], ThresholdError> {
54    let signing_package = SigningPackage::new(commitments_map, tbs);
55
56    let mut signature_shares = BTreeMap::new();
57    for (id, kp, nonces) in signers {
58        let share: SignatureShare = frost::round2::sign(&signing_package, &nonces, kp)?;
59        signature_shares.insert(id, share);
60    }
61
62    let signature = frost::aggregate(&signing_package, &signature_shares, pubkey_package)?;
63    let sig_bytes = signature.serialize()?;
64    let sig_array: [u8; 64] = sig_bytes
65        .try_into()
66        .map_err(|_| ThresholdError::Serialization("expected 64-byte signature".into()))?;
67
68    Ok(sig_array)
69}