• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ergoplatform / sigma-rust / 16405540612

20 Jul 2025 11:50PM UTC coverage: 78.438% (-0.01%) from 78.451%
16405540612

Pull #790

github

web-flow
Merge 7bd76aff4 into 2725f402c
Pull Request #790: Use precomputed tables

62 of 69 new or added lines in 16 files covered. (89.86%)

10 existing lines in 5 files now uncovered.

11961 of 15249 relevant lines covered (78.44%)

2.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.32
/ergotree-interpreter/src/sigma_protocol/private_input.rs
1
//! Private input types for the prover's secrets
2
use core::convert::TryInto;
3
use core::fmt::Formatter;
4

5
use alloc::vec::Vec;
6
use ergo_chain_types::ec_point::exponentiate_gen;
7
use ergo_chain_types::EcPoint;
8
use ergotree_ir::serialization::SigmaSerializable;
9
use ergotree_ir::sigma_protocol::sigma_boolean::ProveDhTuple;
10
use ergotree_ir::sigma_protocol::sigma_boolean::ProveDlog;
11

12
use ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean;
13

14
extern crate derive_more;
15
use derive_more::From;
16
use k256::elliptic_curve::PrimeField;
17
use num_bigint::BigUint;
18
use num_traits::ToPrimitive;
19

20
use 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(from = "Wscalar", into = "Wscalar"))]
25
#[derive(PartialEq, Eq, Clone)]
26
pub struct DlogProverInput {
27
    /// secret key value
28
    pub w: Wscalar,
29
    pk: EcPoint,
30
}
31

32
impl core::fmt::Debug for DlogProverInput {
33
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
×
34
        // to avoid leaking it in error messages, logs, etc.
35
        "DLOGPI:***".fmt(f)
×
36
    }
37
}
38

39
impl From<Wscalar> for DlogProverInput {
40
    fn from(scalar: Wscalar) -> Self {
1✔
41
        DlogProverInput::new(scalar)
1✔
42
    }
43
}
44

45
impl From<DlogProverInput> for Wscalar {
46
    fn from(prover_input: DlogProverInput) -> Self {
1✔
47
        prover_input.w
1✔
48
    }
49
}
50

51
impl DlogProverInput {
52
    /// Scalar(secret key) size in bytes
53
    pub const SIZE_BYTES: usize = 32;
54

55
    /// Create new DlogProverInput
56
    pub fn new(w: Wscalar) -> DlogProverInput {
8✔
57
        Self {
58
            pk: exponentiate_gen(w.as_scalar_ref()),
8✔
59
            w,
60
        }
61
    }
62
    /// generates random secret in the range [0, n), where n is DLog group order.
63
    #[cfg(feature = "std")]
64
    pub fn random() -> DlogProverInput {
4✔
65
        use ergotree_ir::sigma_protocol::dlog_group;
66

67
        use crate::sigma_protocol::crypto_utils;
68

69
        DlogProverInput::new(
70
            dlog_group::random_scalar_in_group_range(crypto_utils::secure_rng()).into(),
4✔
71
        )
72
    }
73

74
    /// Attempts to parse the given byte array as an SEC-1-encoded scalar(secret key).
75
    /// Returns None if the byte array does not contain a big-endian integer in the range [0, modulus).
76
    pub fn from_bytes(bytes: &[u8; DlogProverInput::SIZE_BYTES]) -> Option<DlogProverInput> {
5✔
77
        k256::Scalar::from_repr((*bytes).into())
5✔
78
            .map(|s| DlogProverInput::new(Wscalar::from(s)))
10✔
79
            .into()
80
    }
81

82
    /// Attempts to parse the given Base16-encoded byte array as an SEC-1-encoded scalar(secret key).
83
    /// Returns None if the byte array does not contain a big-endian integer in the range [0, modulus).
84
    pub fn from_base16_str(str: &str) -> Option<DlogProverInput> {
×
85
        base16::decode(str)
×
86
            .ok()
87
            .and_then(|bytes| bytes.as_slice().try_into().ok().map(Self::from_bytes))
×
88
            .flatten()
89
    }
90

91
    /// Attempts to create DlogProverInput from BigUint
92
    /// Returns None if not in the range [0, modulus).
93
    pub fn from_biguint(b: BigUint) -> Option<DlogProverInput> {
4✔
94
        /// Converts a BigUint to a byte array (big-endian).
95
        #[allow(clippy::unwrap_used)]
96
        pub fn biguint_to_32bytes(x: &BigUint) -> [u8; 32] {
4✔
97
            let mask = BigUint::from(u8::MAX);
4✔
98
            let mut bytes = [0u8; 32];
4✔
99
            (0..32).for_each(|i| {
8✔
100
                bytes[i] = ((x >> ((31 - i) * 8)) as BigUint & &mask).to_u8().unwrap();
4✔
101
            });
102
            bytes
4✔
103
        }
104
        let bytes = biguint_to_32bytes(&b);
4✔
105
        Self::from_bytes(&bytes)
4✔
106
    }
107

108
    /// byte representation of the underlying scalar
109
    pub fn to_bytes(&self) -> [u8; DlogProverInput::SIZE_BYTES] {
3✔
110
        self.w.to_bytes()
2✔
111
    }
112

113
    /// public key of discrete logarithm signature protocol
114
    pub fn public_image(&self) -> ProveDlog {
9✔
115
        ProveDlog::new(self.pk)
4✔
116
    }
117

118
    /// Return true if the secret is 0
UNCOV
119
    pub fn is_zero(&self) -> bool {
×
NEW
120
        self.w.is_zero()
×
121
    }
122
}
123

124
/// Diffie-Hellman tuple and secret
125
/// Used in a proof that of equality of discrete logarithms (i.e., a proof of a Diffie-Hellman tuple):
126
/// given group elements g, h, u, v, the proof convinces a verifier that the prover knows `w` such
127
/// that `u = g^w` and `v = h^w`, without revealing `w`
128
#[derive(PartialEq, Eq, Clone)]
129
#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
130
pub struct DhTupleProverInput {
131
    /// Diffie-Hellman tuple's secret
132
    #[cfg_attr(feature = "json", serde(rename = "secret"))]
133
    pub w: Wscalar,
134
    /// Diffie-Hellman tuple
135
    #[cfg_attr(feature = "json", serde(flatten))]
136
    pub common_input: ProveDhTuple,
137
}
138

139
impl core::fmt::Debug for DhTupleProverInput {
140
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
×
141
        // to avoid leaking it in error messages, logs, etc.
142
        "DHTPI:***".fmt(f)
×
143
    }
144
}
145

146
impl DhTupleProverInput {
147
    /// Size in bytes: 32(secret)+33(g)+33(h)+33(u)+33(v)=164 bytes
148
    pub const SIZE_BYTES: usize = DlogProverInput::SIZE_BYTES + EcPoint::GROUP_SIZE * 4;
149

150
    /// Create random secret and Diffie-Hellman tuple
151
    #[allow(clippy::many_single_char_names)]
152
    #[cfg(feature = "std")]
153
    pub fn random() -> DhTupleProverInput {
2✔
154
        use ergo_chain_types::ec_point::{exponentiate, generator};
155
        use ergotree_ir::sigma_protocol::dlog_group;
156
        let h = exponentiate_gen(&dlog_group::random_scalar_in_group_range(
2✔
157
            super::crypto_utils::secure_rng(),
2✔
158
        ));
159
        let w = dlog_group::random_scalar_in_group_range(super::crypto_utils::secure_rng());
2✔
160
        let u = exponentiate_gen(&w);
2✔
161
        let v = exponentiate(&h, &w);
2✔
162
        let common_input = ProveDhTuple::new(generator(), h, u, v);
2✔
163
        DhTupleProverInput {
164
            w: w.into(),
2✔
165
            common_input,
166
        }
167
    }
168

169
    /// Public image (Diffie-Hellman tuple)
170
    pub fn public_image(&self) -> &ProveDhTuple {
2✔
171
        &self.common_input
172
    }
173

174
    /// 32(secret)+33(g)+33(h)+33(u)+33(v)=164 bytes
175
    #[allow(clippy::unwrap_used)]
176
    pub fn to_bytes(&self) -> [u8; DhTupleProverInput::SIZE_BYTES] {
1✔
177
        let mut bytes = Vec::with_capacity(DhTupleProverInput::SIZE_BYTES);
1✔
178
        bytes.extend_from_slice(self.w.as_scalar_ref().to_bytes().as_slice());
2✔
179
        bytes.extend_from_slice(&self.common_input.g.sigma_serialize_bytes().unwrap());
1✔
180
        bytes.extend_from_slice(&self.common_input.h.sigma_serialize_bytes().unwrap());
1✔
181
        bytes.extend_from_slice(&self.common_input.u.sigma_serialize_bytes().unwrap());
1✔
182
        bytes.extend_from_slice(&self.common_input.v.sigma_serialize_bytes().unwrap());
1✔
183
        bytes.try_into().unwrap()
1✔
184
    }
185

186
    /// Parse from bytes (32(secret)+33(g)+33(h)+33(u)+33(v)=164 bytes)
187
    /// secret is expected as SEC-1-encoded scalar of 32 bytes,
188
    /// g,h,u,v are expected as 33-byte compressed points
189
    #[allow(clippy::unwrap_used)]
190
    pub fn from_bytes(bytes: &[u8; DhTupleProverInput::SIZE_BYTES]) -> Option<DhTupleProverInput> {
1✔
191
        let w_bytes: &[u8; DlogProverInput::SIZE_BYTES] =
1✔
192
            &bytes[..DlogProverInput::SIZE_BYTES].try_into().unwrap();
193
        let g_bytes: &[u8; EcPoint::GROUP_SIZE] = &bytes
1✔
194
            [DlogProverInput::SIZE_BYTES..DlogProverInput::SIZE_BYTES + EcPoint::GROUP_SIZE]
1✔
195
            .try_into()
196
            .unwrap();
197
        let h_bytes: &[u8; EcPoint::GROUP_SIZE] = &bytes[DlogProverInput::SIZE_BYTES
2✔
198
            + EcPoint::GROUP_SIZE
199
            ..DlogProverInput::SIZE_BYTES + EcPoint::GROUP_SIZE * 2]
2✔
200
            .try_into()
201
            .unwrap();
202
        let u_bytes: &[u8; EcPoint::GROUP_SIZE] = &bytes[DlogProverInput::SIZE_BYTES
2✔
203
            + EcPoint::GROUP_SIZE * 2
1✔
204
            ..DlogProverInput::SIZE_BYTES + EcPoint::GROUP_SIZE * 3]
2✔
205
            .try_into()
206
            .unwrap();
207
        let v_bytes: &[u8; EcPoint::GROUP_SIZE] = &bytes[DlogProverInput::SIZE_BYTES
2✔
208
            + EcPoint::GROUP_SIZE * 3
1✔
209
            ..DlogProverInput::SIZE_BYTES + EcPoint::GROUP_SIZE * 4]
2✔
210
            .try_into()
211
            .unwrap();
212
        Self::from_bytes_fields(w_bytes, g_bytes, h_bytes, u_bytes, v_bytes)
1✔
213
    }
214

215
    /// Parse from bytes
216
    /// secret is expected as SEC-1-encoded scalar of 32 bytes,
217
    /// g,h,u,v are expected as 33-byte compressed points
218
    pub fn from_bytes_fields(
1✔
219
        w_bytes: &[u8; DlogProverInput::SIZE_BYTES],
220
        g_bytes: &[u8; EcPoint::GROUP_SIZE],
221
        h_bytes: &[u8; EcPoint::GROUP_SIZE],
222
        u_bytes: &[u8; EcPoint::GROUP_SIZE],
223
        v_bytes: &[u8; EcPoint::GROUP_SIZE],
224
    ) -> Option<DhTupleProverInput> {
225
        let w: Option<Wscalar> = k256::Scalar::from_repr((*w_bytes).into())
1✔
226
            .map(Wscalar::from)
227
            .into();
228
        let g = EcPoint::sigma_parse_bytes(&g_bytes[..EcPoint::GROUP_SIZE]).ok()?;
1✔
229
        let h = EcPoint::sigma_parse_bytes(&h_bytes[..EcPoint::GROUP_SIZE]).ok()?;
1✔
230
        let u = EcPoint::sigma_parse_bytes(&u_bytes[..EcPoint::GROUP_SIZE]).ok()?;
1✔
231
        let v = EcPoint::sigma_parse_bytes(&v_bytes[..EcPoint::GROUP_SIZE]).ok()?;
1✔
232
        w.map(|w| DhTupleProverInput {
3✔
233
            w,
234
            common_input: ProveDhTuple::new(g, h, u, v),
1✔
235
        })
236
    }
237
}
238

239
/// Private inputs (secrets)
240
#[derive(PartialEq, Eq, Debug, Clone, From)]
241
pub enum PrivateInput {
242
    /// Discrete logarithm prover input
243
    DlogProverInput(DlogProverInput),
244
    /// Diffie-Hellman tuple prover input
245
    DhTupleProverInput(DhTupleProverInput),
246
}
247

248
impl PrivateInput {
249
    /// Public image of the private input
250
    pub fn public_image(&self) -> SigmaBoolean {
2✔
251
        match self {
2✔
252
            PrivateInput::DlogProverInput(dl) => dl.public_image().into(),
2✔
253
            PrivateInput::DhTupleProverInput(dht) => dht.public_image().clone().into(),
2✔
254
        }
255
    }
256
}
257

258
#[cfg(feature = "arbitrary")]
259
/// Arbitrary impl
260
pub(crate) mod arbitrary {
261

262
    use super::*;
263
    use proptest::prelude::*;
264

265
    impl Arbitrary for DlogProverInput {
266
        type Parameters = ();
267
        type Strategy = BoxedStrategy<Self>;
268
        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
2✔
269
            prop_oneof![
10✔
270
                Just(DlogProverInput::random()),
2✔
271
                Just(DlogProverInput::random()),
4✔
272
                Just(DlogProverInput::random()),
4✔
273
                Just(DlogProverInput::random()),
4✔
274
                Just(DlogProverInput::random()),
4✔
275
            ]
276
            .boxed()
277
        }
278
    }
279

280
    impl Arbitrary for DhTupleProverInput {
281
        type Parameters = ();
282
        type Strategy = BoxedStrategy<Self>;
283
        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1✔
284
            prop_oneof![
5✔
285
                Just(DhTupleProverInput::random()),
1✔
286
                Just(DhTupleProverInput::random()),
2✔
287
                Just(DhTupleProverInput::random()),
2✔
288
                Just(DhTupleProverInput::random()),
2✔
289
                Just(DhTupleProverInput::random()),
2✔
290
            ]
291
            .boxed()
292
        }
293
    }
294

295
    impl Arbitrary for PrivateInput {
296
        type Parameters = ();
297
        type Strategy = BoxedStrategy<Self>;
298
        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1✔
299
            prop_oneof![
2✔
300
                any::<DlogProverInput>().prop_map_into(),
1✔
301
                any::<DhTupleProverInput>().prop_map_into(),
2✔
302
            ]
303
            .boxed()
304
        }
305
    }
306
}
307

308
#[cfg(test)]
309
#[cfg(feature = "arbitrary")]
310
mod tests {}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc