multi_party_ecdsa/utilities/mta/
mod.rs

1/*
2    Multi-party ECDSA
3
4    Copyright 2018 by Kzen Networks
5
6    This file is part of Multi-party ECDSA library
7    (https://github.com/KZen-networks/multi-party-ecdsa)
8
9    Multi-party ECDSA is free software: you can redistribute
10    it and/or modify it under the terms of the GNU General Public
11    License as published by the Free Software Foundation, either
12    version 3 of the License, or (at your option) any later version.
13
14    @license GPL-3.0+ <https://github.com/KZen-networks/multi-party-ecdsa/blob/master/LICENSE>
15*/
16
17/// MtA is described in https://eprint.iacr.org/2019/114.pdf section 3
18use curv::arithmetic::traits::Samplable;
19use curv::cryptographic_primitives::proofs::sigma_dlog::DLogProof;
20use curv::elliptic::curves::{secp256_k1::Secp256k1, Point, Scalar};
21use curv::BigInt;
22use paillier::traits::EncryptWithChosenRandomness;
23use paillier::{Add, Decrypt, Mul};
24use paillier::{DecryptionKey, EncryptionKey, Paillier, Randomness, RawCiphertext, RawPlaintext};
25use zk_paillier::zkproofs::DLogStatement;
26
27use serde::{Deserialize, Serialize};
28use sha2::Sha256;
29
30use crate::protocols::multi_party_ecdsa::gg_2018::party_i::PartyPrivate;
31use crate::utilities::mta::range_proofs::AliceProof;
32use crate::Error::{self, InvalidKey};
33
34#[derive(Clone, Debug, Serialize, Deserialize)]
35pub struct MessageA {
36    pub c: BigInt,                     // paillier encryption
37    pub range_proofs: Vec<AliceProof>, // proofs (using other parties' h1,h2,N_tilde) that the plaintext is small
38}
39
40#[derive(Clone, Debug, Serialize, Deserialize)]
41pub struct MessageB {
42    pub c: BigInt, // paillier encryption
43    pub b_proof: DLogProof<Secp256k1, Sha256>,
44    pub beta_tag_proof: DLogProof<Secp256k1, Sha256>,
45}
46
47impl MessageA {
48    /// Creates a new `messageA` using Alice's Paillier encryption key and `dlog_statements`
49    /// - other parties' `h1,h2,N_tilde`s for range proofs.
50    /// If range proofs are not needed (one example is identification of aborts where we
51    /// only want to reconstruct a ciphertext), `dlog_statements` can be an empty slice.
52    pub fn a(
53        a: &Scalar<Secp256k1>,
54        alice_ek: &EncryptionKey,
55        dlog_statements: &[DLogStatement],
56    ) -> (Self, BigInt) {
57        let randomness = BigInt::sample_below(&alice_ek.n);
58        let m_a = MessageA::a_with_predefined_randomness(a, alice_ek, &randomness, dlog_statements);
59        (m_a, randomness)
60    }
61
62    pub fn a_with_predefined_randomness(
63        a: &Scalar<Secp256k1>,
64        alice_ek: &EncryptionKey,
65        randomness: &BigInt,
66        dlog_statements: &[DLogStatement],
67    ) -> Self {
68        let c_a = Paillier::encrypt_with_chosen_randomness(
69            alice_ek,
70            RawPlaintext::from(a.to_bigint()),
71            &Randomness::from(randomness.clone()),
72        )
73        .0
74        .clone()
75        .into_owned();
76        let alice_range_proofs = dlog_statements
77            .iter()
78            .map(|dlog_statement| {
79                AliceProof::generate(&a.to_bigint(), &c_a, alice_ek, dlog_statement, randomness)
80            })
81            .collect::<Vec<AliceProof>>();
82
83        Self {
84            c: c_a,
85            range_proofs: alice_range_proofs,
86        }
87    }
88}
89
90impl MessageB {
91    pub fn b(
92        b: &Scalar<Secp256k1>,
93        alice_ek: &EncryptionKey,
94        m_a: MessageA,
95        dlog_statements: &[DLogStatement],
96    ) -> Result<(Self, Scalar<Secp256k1>, BigInt, BigInt), Error> {
97        let beta_tag = BigInt::sample_below(&alice_ek.n);
98        let randomness = BigInt::sample_below(&alice_ek.n);
99        let (m_b, beta) = MessageB::b_with_predefined_randomness(
100            b,
101            alice_ek,
102            m_a,
103            &randomness,
104            &beta_tag,
105            dlog_statements,
106        )?;
107
108        Ok((m_b, beta, randomness, beta_tag))
109    }
110
111    pub fn b_with_predefined_randomness(
112        b: &Scalar<Secp256k1>,
113        alice_ek: &EncryptionKey,
114        m_a: MessageA,
115        randomness: &BigInt,
116        beta_tag: &BigInt,
117        dlog_statements: &[DLogStatement],
118    ) -> Result<(Self, Scalar<Secp256k1>), Error> {
119        if m_a.range_proofs.len() != dlog_statements.len() {
120            return Err(InvalidKey);
121        }
122        // verify proofs
123        if !m_a
124            .range_proofs
125            .iter()
126            .zip(dlog_statements)
127            .map(|(proof, dlog_statement)| proof.verify(&m_a.c, alice_ek, dlog_statement))
128            .all(|x| x)
129        {
130            return Err(InvalidKey);
131        };
132        let beta_tag_fe = Scalar::<Secp256k1>::from(beta_tag);
133        let c_beta_tag = Paillier::encrypt_with_chosen_randomness(
134            alice_ek,
135            RawPlaintext::from(beta_tag),
136            &Randomness::from(randomness.clone()),
137        );
138
139        let b_bn = b.to_bigint();
140        let b_c_a = Paillier::mul(
141            alice_ek,
142            RawCiphertext::from(m_a.c),
143            RawPlaintext::from(b_bn),
144        );
145        let c_b = Paillier::add(alice_ek, b_c_a, c_beta_tag);
146        let beta = Scalar::<Secp256k1>::zero() - &beta_tag_fe;
147        let dlog_proof_b = DLogProof::prove(b);
148        let dlog_proof_beta_tag = DLogProof::prove(&beta_tag_fe);
149
150        Ok((
151            Self {
152                c: c_b.0.clone().into_owned(),
153                b_proof: dlog_proof_b,
154                beta_tag_proof: dlog_proof_beta_tag,
155            },
156            beta,
157        ))
158    }
159
160    pub fn verify_proofs_get_alpha(
161        &self,
162        dk: &DecryptionKey,
163        a: &Scalar<Secp256k1>,
164    ) -> Result<(Scalar<Secp256k1>, BigInt), Error> {
165        let alice_share = Paillier::decrypt(dk, &RawCiphertext::from(self.c.clone()));
166        let g = Point::generator();
167        let alpha = Scalar::<Secp256k1>::from(alice_share.0.as_ref());
168        let g_alpha = g * &alpha;
169        let ba_btag = &self.b_proof.pk * a + &self.beta_tag_proof.pk;
170        if DLogProof::verify(&self.b_proof).is_ok()
171            && DLogProof::verify(&self.beta_tag_proof).is_ok()
172            // we prove the correctness of the ciphertext using this check and the proof of knowledge of dlog of beta_tag
173            && ba_btag == g_alpha
174        {
175            Ok((alpha, alice_share.0.into_owned()))
176        } else {
177            Err(InvalidKey)
178        }
179    }
180
181    //  another version, supporting PartyPrivate therefore binding mta to gg18.
182    //  with the regular version mta can be used in general
183    pub fn verify_proofs_get_alpha_gg18(
184        &self,
185        private: &PartyPrivate,
186        a: &Scalar<Secp256k1>,
187    ) -> Result<Scalar<Secp256k1>, Error> {
188        let alice_share = private.decrypt(self.c.clone());
189        let g = Point::generator();
190        let alpha = Scalar::<Secp256k1>::from(alice_share.0.as_ref());
191        let g_alpha = g * &alpha;
192        let ba_btag = &self.b_proof.pk * a + &self.beta_tag_proof.pk;
193
194        if DLogProof::verify(&self.b_proof).is_ok()
195            && DLogProof::verify(&self.beta_tag_proof).is_ok()
196            && ba_btag == g_alpha
197        {
198            Ok(alpha)
199        } else {
200            Err(InvalidKey)
201        }
202    }
203
204    pub fn verify_b_against_public(
205        public_gb: &Point<Secp256k1>,
206        mta_gb: &Point<Secp256k1>,
207    ) -> bool {
208        public_gb == mta_gb
209    }
210}
211
212pub mod range_proofs;
213#[cfg(test)]
214mod test;