ergotree_interpreter/sigma_protocol/
private_input.rs

1//! Private input types for the prover's secrets
2use std::convert::TryInto;
3use std::fmt::Formatter;
4
5use ergo_chain_types::EcPoint;
6use ergotree_ir::serialization::SigmaSerializable;
7use ergotree_ir::sigma_protocol::dlog_group;
8use ergotree_ir::sigma_protocol::sigma_boolean::ProveDhTuple;
9use ergotree_ir::sigma_protocol::sigma_boolean::ProveDlog;
10
11use ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean;
12
13extern crate derive_more;
14use derive_more::From;
15use k256::elliptic_curve::PrimeField;
16use num_bigint::BigUint;
17use num_traits::ToPrimitive;
18
19use super::crypto_utils;
20use super::wscalar::Wscalar;
21
22/// Secret key of discrete logarithm signature protocol
23#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
24#[cfg_attr(feature = "json", serde(transparent))]
25#[derive(PartialEq, Eq, Clone, derive_more::From)]
26pub struct DlogProverInput {
27    /// secret key value
28    pub w: Wscalar,
29}
30
31impl std::fmt::Debug for DlogProverInput {
32    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
33        // to avoid leaking it in error messages, logs, etc.
34        "DLOGPI:***".fmt(f)
35    }
36}
37
38impl DlogProverInput {
39    /// Scalar(secret key) size in bytes
40    pub const SIZE_BYTES: usize = 32;
41
42    /// generates random secret in the range [0, n), where n is DLog group order.
43    pub fn random() -> DlogProverInput {
44        DlogProverInput {
45            w: dlog_group::random_scalar_in_group_range(crypto_utils::secure_rng()).into(),
46        }
47    }
48
49    /// Attempts to parse the given byte array as an SEC-1-encoded scalar(secret key).
50    /// Returns None if the byte array does not contain a big-endian integer in the range [0, modulus).
51    pub fn from_bytes(bytes: &[u8; DlogProverInput::SIZE_BYTES]) -> Option<DlogProverInput> {
52        k256::Scalar::from_repr((*bytes).into())
53            .map(|s| DlogProverInput::from(Wscalar::from(s)))
54            .into()
55    }
56
57    /// Attempts to parse the given Base16-encoded byte array as an SEC-1-encoded scalar(secret key).
58    /// Returns None if the byte array does not contain a big-endian integer in the range [0, modulus).
59    pub fn from_base16_str(str: String) -> Option<DlogProverInput> {
60        base16::decode(&str)
61            .ok()
62            .and_then(|bytes| bytes.as_slice().try_into().ok().map(Self::from_bytes))
63            .flatten()
64    }
65
66    /// Attempts to create DlogProverInput from BigUint
67    /// Returns None if not in the range [0, modulus).
68    pub fn from_biguint(b: BigUint) -> Option<DlogProverInput> {
69        /// Converts a BigUint to a byte array (big-endian).
70        #[allow(clippy::unwrap_used)]
71        pub fn biguint_to_32bytes(x: &BigUint) -> [u8; 32] {
72            let mask = BigUint::from(u8::MAX);
73            let mut bytes = [0u8; 32];
74            (0..32).for_each(|i| {
75                bytes[i] = ((x >> ((31 - i) * 8)) as BigUint & &mask).to_u8().unwrap();
76            });
77            bytes
78        }
79        let bytes = biguint_to_32bytes(&b);
80        Self::from_bytes(&bytes)
81    }
82
83    /// byte representation of the underlying scalar
84    pub fn to_bytes(&self) -> [u8; DlogProverInput::SIZE_BYTES] {
85        self.w.as_scalar_ref().to_bytes().into()
86    }
87
88    /// public key of discrete logarithm signature protocol
89    pub fn public_image(&self) -> ProveDlog {
90        // test it, see https://github.com/ergoplatform/sigma-rust/issues/38
91        let g = ergo_chain_types::ec_point::generator();
92        ProveDlog::new(ergo_chain_types::ec_point::exponentiate(
93            &g,
94            self.w.as_scalar_ref(),
95        ))
96    }
97
98    /// Return true if the secret is 0
99    pub fn is_zero(&self) -> bool {
100        self.w.as_scalar_ref().is_zero().into()
101    }
102}
103
104/// Diffie-Hellman tuple and secret
105/// Used in a proof that of equality of discrete logarithms (i.e., a proof of a Diffie-Hellman tuple):
106/// given group elements g, h, u, v, the proof convinces a verifier that the prover knows `w` such
107/// that `u = g^w` and `v = h^w`, without revealing `w`
108#[derive(PartialEq, Eq, Clone)]
109#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
110pub struct DhTupleProverInput {
111    /// Diffie-Hellman tuple's secret
112    #[cfg_attr(feature = "json", serde(rename = "secret"))]
113    pub w: Wscalar,
114    /// Diffie-Hellman tuple
115    #[cfg_attr(feature = "json", serde(flatten))]
116    pub common_input: ProveDhTuple,
117}
118
119impl std::fmt::Debug for DhTupleProverInput {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        // to avoid leaking it in error messages, logs, etc.
122        "DHTPI:***".fmt(f)
123    }
124}
125
126impl DhTupleProverInput {
127    /// Size in bytes: 32(secret)+33(g)+33(h)+33(u)+33(v)=164 bytes
128    pub const SIZE_BYTES: usize = DlogProverInput::SIZE_BYTES + EcPoint::GROUP_SIZE * 4;
129
130    /// Create random secret and Diffie-Hellman tuple
131    #[allow(clippy::many_single_char_names)]
132    pub fn random() -> DhTupleProverInput {
133        use ergo_chain_types::ec_point::{exponentiate, generator};
134        let g = generator();
135        let h = exponentiate(
136            &generator(),
137            &dlog_group::random_scalar_in_group_range(crypto_utils::secure_rng()),
138        );
139        let w = dlog_group::random_scalar_in_group_range(crypto_utils::secure_rng());
140        let u = exponentiate(&g, &w);
141        let v = exponentiate(&h, &w);
142        let common_input = ProveDhTuple::new(g, h, u, v);
143        DhTupleProverInput {
144            w: w.into(),
145            common_input,
146        }
147    }
148
149    /// Public image (Diffie-Hellman tuple)
150    pub fn public_image(&self) -> &ProveDhTuple {
151        &self.common_input
152    }
153
154    /// 32(secret)+33(g)+33(h)+33(u)+33(v)=164 bytes
155    #[allow(clippy::unwrap_used)]
156    pub fn to_bytes(&self) -> [u8; DhTupleProverInput::SIZE_BYTES] {
157        let mut bytes = Vec::with_capacity(DhTupleProverInput::SIZE_BYTES);
158        bytes.extend_from_slice(self.w.as_scalar_ref().to_bytes().as_slice());
159        bytes.extend_from_slice(&self.common_input.g.sigma_serialize_bytes().unwrap());
160        bytes.extend_from_slice(&self.common_input.h.sigma_serialize_bytes().unwrap());
161        bytes.extend_from_slice(&self.common_input.u.sigma_serialize_bytes().unwrap());
162        bytes.extend_from_slice(&self.common_input.v.sigma_serialize_bytes().unwrap());
163        bytes.try_into().unwrap()
164    }
165
166    /// Parse from bytes (32(secret)+33(g)+33(h)+33(u)+33(v)=164 bytes)
167    /// secret is expected as SEC-1-encoded scalar of 32 bytes,
168    /// g,h,u,v are expected as 33-byte compressed points
169    #[allow(clippy::unwrap_used)]
170    pub fn from_bytes(bytes: &[u8; DhTupleProverInput::SIZE_BYTES]) -> Option<DhTupleProverInput> {
171        let w_bytes: &[u8; DlogProverInput::SIZE_BYTES] =
172            &bytes[..DlogProverInput::SIZE_BYTES].try_into().unwrap();
173        let g_bytes: &[u8; EcPoint::GROUP_SIZE] = &bytes
174            [DlogProverInput::SIZE_BYTES..DlogProverInput::SIZE_BYTES + EcPoint::GROUP_SIZE]
175            .try_into()
176            .unwrap();
177        let h_bytes: &[u8; EcPoint::GROUP_SIZE] = &bytes[DlogProverInput::SIZE_BYTES
178            + EcPoint::GROUP_SIZE
179            ..DlogProverInput::SIZE_BYTES + EcPoint::GROUP_SIZE * 2]
180            .try_into()
181            .unwrap();
182        let u_bytes: &[u8; EcPoint::GROUP_SIZE] = &bytes[DlogProverInput::SIZE_BYTES
183            + EcPoint::GROUP_SIZE * 2
184            ..DlogProverInput::SIZE_BYTES + EcPoint::GROUP_SIZE * 3]
185            .try_into()
186            .unwrap();
187        let v_bytes: &[u8; EcPoint::GROUP_SIZE] = &bytes[DlogProverInput::SIZE_BYTES
188            + EcPoint::GROUP_SIZE * 3
189            ..DlogProverInput::SIZE_BYTES + EcPoint::GROUP_SIZE * 4]
190            .try_into()
191            .unwrap();
192        Self::from_bytes_fields(w_bytes, g_bytes, h_bytes, u_bytes, v_bytes)
193    }
194
195    /// Parse from bytes
196    /// secret is expected as SEC-1-encoded scalar of 32 bytes,
197    /// g,h,u,v are expected as 33-byte compressed points
198    pub fn from_bytes_fields(
199        w_bytes: &[u8; DlogProverInput::SIZE_BYTES],
200        g_bytes: &[u8; EcPoint::GROUP_SIZE],
201        h_bytes: &[u8; EcPoint::GROUP_SIZE],
202        u_bytes: &[u8; EcPoint::GROUP_SIZE],
203        v_bytes: &[u8; EcPoint::GROUP_SIZE],
204    ) -> Option<DhTupleProverInput> {
205        let w: Option<Wscalar> = k256::Scalar::from_repr((*w_bytes).into())
206            .map(Wscalar::from)
207            .into();
208        let g = EcPoint::sigma_parse_bytes(&g_bytes[..EcPoint::GROUP_SIZE]).ok()?;
209        let h = EcPoint::sigma_parse_bytes(&h_bytes[..EcPoint::GROUP_SIZE]).ok()?;
210        let u = EcPoint::sigma_parse_bytes(&u_bytes[..EcPoint::GROUP_SIZE]).ok()?;
211        let v = EcPoint::sigma_parse_bytes(&v_bytes[..EcPoint::GROUP_SIZE]).ok()?;
212        w.map(|w| DhTupleProverInput {
213            w,
214            common_input: ProveDhTuple::new(g, h, u, v),
215        })
216    }
217}
218
219/// Private inputs (secrets)
220#[derive(PartialEq, Eq, Debug, Clone, From)]
221pub enum PrivateInput {
222    /// Discrete logarithm prover input
223    DlogProverInput(DlogProverInput),
224    /// Diffie-Hellman tuple prover input
225    DhTupleProverInput(DhTupleProverInput),
226}
227
228impl PrivateInput {
229    /// Public image of the private input
230    pub fn public_image(&self) -> SigmaBoolean {
231        match self {
232            PrivateInput::DlogProverInput(dl) => dl.public_image().into(),
233            PrivateInput::DhTupleProverInput(dht) => dht.public_image().clone().into(),
234        }
235    }
236}
237
238#[cfg(feature = "arbitrary")]
239/// Arbitrary impl
240pub(crate) mod arbitrary {
241
242    use super::*;
243    use proptest::prelude::*;
244
245    impl Arbitrary for DlogProverInput {
246        type Parameters = ();
247        type Strategy = BoxedStrategy<Self>;
248        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
249            prop_oneof![
250                Just(DlogProverInput::random()),
251                Just(DlogProverInput::random()),
252                Just(DlogProverInput::random()),
253                Just(DlogProverInput::random()),
254                Just(DlogProverInput::random()),
255            ]
256            .boxed()
257        }
258    }
259
260    impl Arbitrary for DhTupleProverInput {
261        type Parameters = ();
262        type Strategy = BoxedStrategy<Self>;
263        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
264            prop_oneof![
265                Just(DhTupleProverInput::random()),
266                Just(DhTupleProverInput::random()),
267                Just(DhTupleProverInput::random()),
268                Just(DhTupleProverInput::random()),
269                Just(DhTupleProverInput::random()),
270            ]
271            .boxed()
272        }
273    }
274
275    impl Arbitrary for PrivateInput {
276        type Parameters = ();
277        type Strategy = BoxedStrategy<Self>;
278        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
279            prop_oneof![
280                any::<DlogProverInput>().prop_map_into(),
281                any::<DhTupleProverInput>().prop_map_into(),
282            ]
283            .boxed()
284        }
285    }
286}
287
288#[cfg(test)]
289#[cfg(feature = "arbitrary")]
290mod tests {}