coconut_crypto/proof/messages_pok/
proof.rs

1use alloc::vec::Vec;
2use ark_ec::pairing::Pairing;
3use ark_serialize::*;
4
5use serde::{Deserialize, Serialize};
6use utils::try_iter::InvalidPair;
7
8use crate::{
9    helpers::{
10        pluck_missed, seq_pairs_satisfy, take_while_satisfy, DoubleEndedExactSizeIterator,
11        SendIfParallel, WithSchnorrResponse,
12    },
13    setup::SignatureParams,
14};
15#[cfg(feature = "parallel")]
16use rayon::prelude::*;
17
18use super::*;
19
20/// Proof of knowledge for the messages.
21#[derive(
22    Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize,
23)]
24pub struct MessagesPoK<E: Pairing> {
25    /// `com = g * o + \sum_{i}(h_{i} * m_{i})`
26    pub(super) com_resp: WithSchnorrResponse<E::G1Affine, MultiMessageCommitment<E>>,
27    /// `com_{j} = g * o_{j} + h * m_{j}`
28    pub(super) com_j_resp: Vec<WithSchnorrResponse<E::G1Affine, MessageCommitment<E>>>,
29}
30
31impl<E: Pairing> MessagesPoK<E> {
32    /// Verifies underlying proof of knowledge using supplied arguments.
33    /// `unique_sorted_revealed_indices` must produce sorted unique indices, otherwise, an error will be returned.
34    pub fn verify<I>(
35        &self,
36        challenge: &E::ScalarField,
37        unique_sorted_revealed_indices: I,
38        params: &SignatureParams<E>,
39        h: &E::G1Affine,
40    ) -> Result<()>
41    where
42        I: IntoIterator<Item = usize> + SendIfParallel,
43    {
44        let (eq_res, com_res, com_j_res) = join!(
45            // Verify equality of the corresponding Schnorr responses for `m_{i}` in both commitments
46            self.verify_responses(),
47            // Verify relation `com = g * o + \sum_{i}(h_{i} * m_{i})`
48            self.verify_com(challenge, unique_sorted_revealed_indices, params),
49            // Verify relation `com_{j} = g * o_{j} + h * m_{j}`
50            self.verify_com_j(challenge, params, h)
51        );
52
53        eq_res.and(com_res).and(com_j_res)
54    }
55
56    /// The commitment's contribution to the overall challenge of the protocol.
57    pub fn challenge_contribution<W: Write>(
58        &self,
59        mut writer: W,
60        &SignatureParams {
61            g, h: ref h_arr, ..
62        }: &SignatureParams<E>,
63        h: &E::G1Affine,
64    ) -> Result<(), SchnorrError> {
65        // `com = g * o + \sum_{i}(h_{i} * m_{i})`
66        g.serialize_compressed(&mut writer)?;
67        h_arr.serialize_compressed(&mut writer)?;
68        self.com_resp.challenge_contribution(&mut writer)?;
69
70        // `com_{j} = g * o_{j} + h * m_{j}`
71        h.serialize_compressed(&mut writer)?;
72        for com_j in &self.com_j_resp {
73            com_j.challenge_contribution(&mut writer)?;
74        }
75
76        Ok(())
77    }
78
79    /// Returns underlying message commitments.
80    pub fn commitments(
81        &self,
82    ) -> impl DoubleEndedExactSizeIterator<Item = &MessageCommitment<E>> + Clone + '_ {
83        self.com_j_resp.iter().map(|resp| &resp.value)
84    }
85
86    /// Verifies equality of the corresponding Schnorr responses for `m_{i}` in both commitments.
87    fn verify_responses(&self) -> Result<()> {
88        if self.com_resp.response.0.len() != self.com_j_resp.len() + 1 {
89            Err(MessagesPoKError::SchnorrResponsesHaveDifferentLength)?
90        }
91
92        let m_i_resp = cfg_iter!(self.com_resp.response.0).skip(1).map(Some);
93        let m_j_resp = cfg_iter!(self.com_j_resp).map(|resp| resp.response.0.get(1));
94
95        #[cfg(feature = "parallel")]
96        let find_map = ParallelIterator::find_map_any;
97        #[cfg(not(feature = "parallel"))]
98        let find_map = |mut iter, f| Iterator::find_map(&mut iter, f);
99
100        let invalid_idx = find_map(
101            m_i_resp.zip(m_j_resp).enumerate(),
102            |(idx, (m_i_resp, m_j_resp))| (m_i_resp != m_j_resp).then_some(idx),
103        );
104
105        if let Some(idx) = invalid_idx {
106            Err(MessagesPoKError::SchnorrResponsesNotEqual(idx))
107        } else {
108            Ok(())
109        }
110    }
111
112    /// Verifies relation `com = g * o + \sum_{i}(h_{i} * m_{i})`
113    fn verify_com(
114        &self,
115        challenge: &E::ScalarField,
116        unique_sorted_revealed_indices: impl IntoIterator<Item = usize>,
117        SignatureParams { g, h, .. }: &SignatureParams<E>,
118    ) -> Result<()> {
119        // This option may contain an invalid pair of previous - current indices at the end of the iteration
120        let mut invalid_idx_pair = None;
121        // Pick only committed `h` using supplied indices of the revealed messages
122        let committed_h = pluck_missed(
123            take_while_satisfy(
124                unique_sorted_revealed_indices,
125                seq_pairs_satisfy(|a, b| a < b),
126                &mut invalid_idx_pair,
127            ),
128            h,
129        );
130        let verification_res = self
131            .com_resp
132            .verify_challenge(challenge, g, committed_h)
133            .map_err(schnorr_error)
134            .map_err(MessagesPoKError::InvalidComProof);
135
136        if let Some(InvalidPair(previous, current)) = invalid_idx_pair {
137            Err(MessagesPoKError::RevealedIndicesMustBeUniqueAndSorted { previous, current })
138        } else {
139            verification_res
140        }
141    }
142
143    /// Verifies relation `com_{j} = g * o_{j} + h * m_{j}`
144    fn verify_com_j(
145        &self,
146        challenge: &E::ScalarField,
147        SignatureParams { g, .. }: &SignatureParams<E>,
148        h: &E::G1Affine,
149    ) -> Result<()> {
150        cfg_iter!(self.com_j_resp)
151            .enumerate()
152            .map(|(index, com_j)| {
153                com_j
154                    .verify_challenge(challenge, g, h)
155                    .map_err(schnorr_error)
156                    .map_err(|error| MessagesPoKError::InvalidComJProof { index, error })
157            })
158            .collect()
159    }
160}