threshold_bls/sig/
tblind.rs

1use thiserror::Error;
2
3use crate::poly::{Eval, Poly};
4use crate::sig::tbls::Share;
5use crate::sig::{BlindScheme, BlindThresholdScheme, Partial, ThresholdScheme};
6
7#[derive(Debug, Error)]
8// TODO: Can we get rid of this static lifetime bound?
9/// Errors associated with partially unblinding a signature
10pub enum BlindThresholdError<E: 'static + std::error::Error> {
11    /// Raised when unblinding fails
12    #[error(transparent)]
13    BlindError(E),
14
15    /// Raised when (de)serialization fails
16    #[error(transparent)]
17    BincodeError(#[from] bincode::Error),
18}
19
20impl<T> BlindThresholdScheme for T
21where
22    T: 'static + ThresholdScheme + BlindScheme,
23{
24    type Error = BlindThresholdError<<T as BlindScheme>::Error>;
25
26    fn sign_blind_partial(
27        private: &Share<Self::Private>,
28        blinded_msg: &[u8],
29    ) -> Result<Partial, <Self as BlindThresholdScheme>::Error> {
30        let sig = Self::blind_sign(&private.private, blinded_msg)
31            .map_err(BlindThresholdError::BlindError)?;
32        let partial = Eval {
33            value: sig,
34            index: private.index,
35        };
36        bincode::serialize(&partial).map_err(BlindThresholdError::BincodeError)
37    }
38
39    fn unblind_partial_sig(
40        t: &Self::Token,
41        partial: &[u8],
42    ) -> Result<Partial, <Self as BlindThresholdScheme>::Error> {
43        // deserialize the sig
44        let partial: Eval<Vec<u8>> = bincode::deserialize(partial)?;
45
46        let partially_unblinded =
47            Self::unblind_sig(t, &partial.value).map_err(BlindThresholdError::BlindError)?;
48        let partially_unblinded = Eval {
49            index: partial.index,
50            value: partially_unblinded,
51        };
52        bincode::serialize(&partially_unblinded).map_err(BlindThresholdError::BincodeError)
53    }
54
55    fn verify_blind_partial(
56        public: &Poly<Self::Public>,
57        blind_msg: &[u8],
58        blind_partial: &[u8],
59    ) -> Result<(), <Self as BlindThresholdScheme>::Error> {
60        let blinded_partial: Eval<Vec<u8>> = bincode::deserialize(blind_partial)?;
61        let public_i = public.eval(blinded_partial.index);
62        Self::blind_verify(&public_i.value, blind_msg, &blinded_partial.value)
63            .map_err(BlindThresholdError::BlindError)
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use rand::thread_rng;
70
71    #[cfg(feature = "bls12_381")]
72    use crate::curve::bls12381::PairingCurve as PCurve;
73    use crate::poly::Idx;
74    use crate::sig::{
75        bls::{G1Scheme, G2Scheme},
76        SignatureScheme,
77    };
78
79    use super::*;
80
81    fn shares<B: BlindThresholdScheme>(
82        n: usize,
83        t: usize,
84    ) -> (Vec<Share<B::Private>>, Poly<B::Public>) {
85        let private = Poly::<B::Private>::new(t - 1);
86        let shares = (0..n)
87            .map(|i| private.eval(i as Idx))
88            .map(|e| Share {
89                index: e.index,
90                private: e.value,
91            })
92            .collect();
93        (shares, private.commit())
94    }
95
96    #[cfg(feature = "bls12_381")]
97    #[test]
98    fn tblind_g1_bellman_unblind() {
99        tblind_test::<G1Scheme<PCurve>>();
100    }
101
102    #[cfg(feature = "bls12_381")]
103    #[test]
104    fn tblind_g2_bellman_unblind() {
105        tblind_test::<G2Scheme<PCurve>>();
106    }
107
108    fn tblind_test<B>()
109    where
110        B: BlindThresholdScheme + SignatureScheme + ThresholdScheme,
111    {
112        let n = 5;
113        let thr = 4;
114        let (shares, public) = shares::<B>(n, thr);
115        let msg = vec![1, 9, 6, 9];
116
117        // blind the msg
118        let (token, blinded) = B::blind_msg(&msg, &mut thread_rng());
119
120        // partially sign it
121        let partials: Vec<_> = shares
122            .iter()
123            .map(|share| B::sign_blind_partial(share, &blinded).unwrap())
124            .collect();
125
126        // verify if each blind partial signatures is correct
127        assert_eq!(
128            false,
129            partials
130                .iter()
131                .any(|p| B::verify_blind_partial(&public, &blinded, p).is_err())
132        );
133
134        // unblind each partial sig
135        let unblindeds_partials: Vec<_> = partials
136            .iter()
137            .map(|p| B::unblind_partial_sig(&token, p).unwrap())
138            .collect();
139
140        // aggregate & verify the unblinded partials
141        let final_sig1 = B::aggregate(thr, &unblindeds_partials).unwrap();
142        B::verify(&public.public_key(), &msg, &final_sig1).unwrap();
143
144        // Another method is to aggregate the blinded partials directly. This
145        // can be done by a third party
146        let blinded_final = B::aggregate(thr, &partials).unwrap();
147
148        // unblind the aggregated signature
149        let final_sig2 = B::unblind_sig(&token, &blinded_final).unwrap();
150
151        // verify the final signature
152        B::verify(&public.public_key(), &msg, &final_sig2).unwrap();
153        assert_eq!(final_sig1, final_sig2);
154    }
155}