Skip to main content

blsful/
proof_commitment.rs

1use crate::impls::inner_types::*;
2use crate::*;
3use rand::Rng;
4use rand_core::{CryptoRng, RngCore};
5use subtle::CtOption;
6
7/// The commitment portion of the signature proof of knowledge
8#[derive(PartialEq, Eq, serde::Serialize, serde::Deserialize)]
9pub enum ProofCommitment<C: BlsSignatureImpl> {
10    /// The basic signature scheme
11    Basic(
12        /// The commitment
13        #[serde(serialize_with = "traits::signature::serialize::<C, _>")]
14        #[serde(deserialize_with = "traits::signature::deserialize::<C, _>")]
15        <C as Pairing>::Signature,
16    ),
17    /// The message augmentation signature scheme
18    MessageAugmentation(
19        /// The commitment
20        #[serde(serialize_with = "traits::signature::serialize::<C, _>")]
21        #[serde(deserialize_with = "traits::signature::deserialize::<C, _>")]
22        <C as Pairing>::Signature,
23    ),
24    /// The proof of possession signature scheme
25    ProofOfPossession(
26        /// The commitment
27        #[serde(serialize_with = "traits::signature::serialize::<C, _>")]
28        #[serde(deserialize_with = "traits::signature::deserialize::<C, _>")]
29        <C as Pairing>::Signature,
30    ),
31}
32
33impl<C: BlsSignatureImpl> Default for ProofCommitment<C> {
34    fn default() -> Self {
35        Self::ProofOfPossession(<C as Pairing>::Signature::default())
36    }
37}
38
39impl<C: BlsSignatureImpl> Display for ProofCommitment<C> {
40    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
41        match self {
42            Self::Basic(s) => write!(f, "Basic({})", s),
43            Self::MessageAugmentation(s) => write!(f, "MessageAugmentation({})", s),
44            Self::ProofOfPossession(s) => write!(f, "ProofOfPossession({})", s),
45        }
46    }
47}
48
49impl<C: BlsSignatureImpl> fmt::Debug for ProofCommitment<C> {
50    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
51        match self {
52            Self::Basic(s) => write!(f, "Basic({:?})", s),
53            Self::MessageAugmentation(s) => write!(f, "MessageAugmentation({:?})", s),
54            Self::ProofOfPossession(s) => write!(f, "ProofOfPossession({:?})", s),
55        }
56    }
57}
58
59impl<C: BlsSignatureImpl> Copy for ProofCommitment<C> {}
60
61impl<C: BlsSignatureImpl> Clone for ProofCommitment<C> {
62    fn clone(&self) -> Self {
63        *self
64    }
65}
66
67impl<C: BlsSignatureImpl> subtle::ConditionallySelectable for ProofCommitment<C> {
68    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
69        match (a, b) {
70            (Self::Basic(a), Self::Basic(b)) => {
71                Self::Basic(<C as Pairing>::Signature::conditional_select(a, b, choice))
72            }
73            (Self::MessageAugmentation(a), Self::MessageAugmentation(b)) => {
74                Self::MessageAugmentation(<C as Pairing>::Signature::conditional_select(
75                    a, b, choice,
76                ))
77            }
78            (Self::ProofOfPossession(a), Self::ProofOfPossession(b)) => {
79                Self::ProofOfPossession(<C as Pairing>::Signature::conditional_select(a, b, choice))
80            }
81            _ => panic!("Cannot conditional select between different proof commitments"),
82        }
83    }
84}
85
86impl_from_derivatives_generic!(ProofCommitment);
87
88impl<C: BlsSignatureImpl> From<&ProofCommitment<C>> for Vec<u8> {
89    fn from(value: &ProofCommitment<C>) -> Self {
90        serde_bare::to_vec(value).unwrap()
91    }
92}
93
94impl<C: BlsSignatureImpl> TryFrom<&[u8]> for ProofCommitment<C> {
95    type Error = BlsError;
96
97    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
98        let len = C::Signature::default().to_bytes().as_ref().len() + 1;
99        if value.len() != len {
100            return Err(BlsError::InvalidInputs(format!(
101                "Invalid length, expected {}, got {}",
102                len,
103                value.len()
104            )));
105        }
106        serde_bare::from_slice(value).map_err(|e| BlsError::InvalidInputs(e.to_string()))
107    }
108}
109
110impl<C: BlsSignatureImpl> ProofCommitment<C> {
111    /// Generate a new proof of knowledge commitment
112    /// This is step 1 in the 3 step process
113    pub fn generate<B: AsRef<[u8]>>(
114        msg: B,
115        signature: Signature<C>,
116    ) -> BlsResult<(Self, ProofCommitmentSecret<C>)> {
117        match signature {
118            Signature::Basic(_) => {
119                let (u, x) = <C as BlsSignatureProof>::generate_commitment(
120                    msg,
121                    <C as BlsSignatureBasic>::DST,
122                )?;
123                Ok((Self::Basic(u), ProofCommitmentSecret(x)))
124            }
125            Signature::MessageAugmentation(_) => {
126                let (u, x) = <C as BlsSignatureProof>::generate_commitment(
127                    msg,
128                    <C as BlsSignatureMessageAugmentation>::DST,
129                )?;
130                Ok((Self::MessageAugmentation(u), ProofCommitmentSecret(x)))
131            }
132            Signature::ProofOfPossession(_) => {
133                let (u, x) = <C as BlsSignatureProof>::generate_commitment(
134                    msg,
135                    <C as BlsSignaturePop>::SIG_DST,
136                )?;
137                Ok((Self::ProofOfPossession(u), ProofCommitmentSecret(x)))
138            }
139        }
140    }
141
142    /// Finish the commitment value by converting it into a proof of knowledge
143    /// Step 3 in the 3 step process
144    pub fn finalize(
145        self,
146        x: ProofCommitmentSecret<C>,
147        y: ProofCommitmentChallenge<C>,
148        sig: Signature<C>,
149    ) -> BlsResult<ProofOfKnowledge<C>> {
150        match (self, sig) {
151            (Self::Basic(u), Signature::Basic(s)) => {
152                let (u, v) = <C as BlsSignatureProof>::generate_proof(u, x.0, y.0, s)?;
153                Ok(ProofOfKnowledge::Basic { u, v })
154            }
155            (Self::MessageAugmentation(u), Signature::MessageAugmentation(s)) => {
156                let (u, v) = <C as BlsSignatureProof>::generate_proof(u, x.0, y.0, s)?;
157                Ok(ProofOfKnowledge::MessageAugmentation { u, v })
158            }
159            (Self::ProofOfPossession(u), Signature::ProofOfPossession(s)) => {
160                let (u, v) = <C as BlsSignatureProof>::generate_proof(u, x.0, y.0, s)?;
161                Ok(ProofOfKnowledge::ProofOfPossession { u, v })
162            }
163            (_, _) => Err(BlsError::InvalidProof),
164        }
165    }
166}
167
168/// A commitment secret used to create the proof of knowledge
169#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
170pub struct ProofCommitmentSecret<C: BlsSignatureImpl>(
171    /// The commitment secret raw value
172    #[serde(serialize_with = "traits::scalar::serialize::<C, _>")]
173    #[serde(deserialize_with = "traits::scalar::deserialize::<C, _>")]
174    pub <<C as Pairing>::PublicKey as Group>::Scalar,
175);
176
177impl_from_derivatives_generic!(ProofCommitmentSecret);
178
179impl<C: BlsSignatureImpl> From<&ProofCommitmentSecret<C>> for Vec<u8> {
180    fn from(value: &ProofCommitmentSecret<C>) -> Self {
181        scalar_to_be_bytes::<C, SECRET_KEY_BYTES>(value.0).to_vec()
182    }
183}
184
185impl<C: BlsSignatureImpl> TryFrom<&[u8]> for ProofCommitmentSecret<C> {
186    type Error = BlsError;
187
188    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
189        let bytes = <[u8; 32]>::try_from(value)
190            .map_err(|_| BlsError::InvalidInputs("Invalid secret key bytes".to_string()))?;
191        let value = scalar_from_be_bytes::<C, SECRET_KEY_BYTES>(&bytes).map(Self);
192        Option::from(value)
193            .ok_or_else(|| BlsError::InvalidInputs("Invalid secret key bytes".to_string()))
194    }
195}
196
197impl<C: BlsSignatureImpl> ProofCommitmentSecret<C> {
198    /// Get the big-endian byte representation of this key
199    pub fn to_be_bytes(&self) -> [u8; SECRET_KEY_BYTES] {
200        scalar_to_be_bytes::<C, SECRET_KEY_BYTES>(self.0)
201    }
202
203    /// Get the little-endian byte representation of this key
204    pub fn to_le_bytes(&self) -> [u8; SECRET_KEY_BYTES] {
205        scalar_to_le_bytes::<C, SECRET_KEY_BYTES>(self.0)
206    }
207
208    /// Convert a big-endian representation of the secret key.
209    pub fn from_be_bytes(bytes: &[u8; SECRET_KEY_BYTES]) -> CtOption<Self> {
210        scalar_from_be_bytes::<C, SECRET_KEY_BYTES>(bytes).map(Self)
211    }
212
213    /// Convert a little-endian representation of the secret key.
214    pub fn from_le_bytes(bytes: &[u8; SECRET_KEY_BYTES]) -> CtOption<Self> {
215        scalar_from_le_bytes::<C, SECRET_KEY_BYTES>(bytes).map(Self)
216    }
217}
218
219/// The proof of knowledge challenge value generated by the server in
220/// step 2 of the proof generation process
221#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
222pub struct ProofCommitmentChallenge<C: BlsSignatureImpl>(
223    /// The commitment challenge raw value
224    #[serde(serialize_with = "traits::scalar::serialize::<C, _>")]
225    #[serde(deserialize_with = "traits::scalar::deserialize::<C, _>")]
226    pub <<C as Pairing>::PublicKey as Group>::Scalar,
227);
228
229impl_from_derivatives_generic!(ProofCommitmentChallenge);
230
231impl<C: BlsSignatureImpl> From<&ProofCommitmentChallenge<C>> for Vec<u8> {
232    fn from(value: &ProofCommitmentChallenge<C>) -> Self {
233        scalar_to_be_bytes::<C, SECRET_KEY_BYTES>(value.0).to_vec()
234    }
235}
236
237impl<C: BlsSignatureImpl> TryFrom<&[u8]> for ProofCommitmentChallenge<C> {
238    type Error = BlsError;
239
240    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
241        let bytes = <[u8; 32]>::try_from(value)
242            .map_err(|_| BlsError::InvalidInputs("Invalid secret key bytes".to_string()))?;
243        let value = scalar_from_be_bytes::<C, SECRET_KEY_BYTES>(&bytes).map(Self);
244        Option::from(value)
245            .ok_or_else(|| BlsError::InvalidInputs("Invalid secret key bytes".to_string()))
246    }
247}
248
249impl<C: BlsSignatureImpl> ProofCommitmentChallenge<C> {
250    /// Create a new random secret key
251    pub fn new() -> Self {
252        Self::random(get_crypto_rng())
253    }
254
255    /// Compute a secret key from a hash
256    pub fn from_hash<B: AsRef<[u8]>>(data: B) -> Self {
257        Self(<C as HashToScalar>::hash_to_scalar(
258            data.as_ref(),
259            KEYGEN_SALT,
260        ))
261    }
262
263    /// Compute a random challenge from a CS-PRNG
264    pub fn random(mut rng: impl RngCore + CryptoRng) -> Self {
265        Self(<C as HashToScalar>::hash_to_scalar(
266            rng.r#gen::<[u8; SECRET_KEY_BYTES]>(),
267            KEYGEN_SALT,
268        ))
269    }
270
271    /// Get the big-endian byte representation of this key
272    pub fn to_be_bytes(&self) -> [u8; SECRET_KEY_BYTES] {
273        scalar_to_be_bytes::<C, SECRET_KEY_BYTES>(self.0)
274    }
275
276    /// Get the little-endian byte representation of this key
277    pub fn to_le_bytes(&self) -> [u8; SECRET_KEY_BYTES] {
278        scalar_to_le_bytes::<C, SECRET_KEY_BYTES>(self.0)
279    }
280
281    /// Convert a big-endian representation of the secret key.
282    pub fn from_be_bytes(bytes: &[u8; SECRET_KEY_BYTES]) -> CtOption<Self> {
283        scalar_from_be_bytes::<C, SECRET_KEY_BYTES>(bytes).map(Self)
284    }
285
286    /// Convert a little-endian representation of the secret key.
287    pub fn from_le_bytes(bytes: &[u8; SECRET_KEY_BYTES]) -> CtOption<Self> {
288        scalar_from_le_bytes::<C, SECRET_KEY_BYTES>(bytes).map(Self)
289    }
290}