Skip to content

Commit

Permalink
feat: Add InvalidModProof & InvalidFacProof on-chain verification (
Browse files Browse the repository at this point in the history
…#470)

* Add InvalidModProof verification

* split checks

* fix clippy issues
  • Loading branch information
shekohex authored Feb 14, 2024
1 parent a2d9ba7 commit 7d50289
Show file tree
Hide file tree
Showing 11 changed files with 718 additions and 21 deletions.
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
];
packages = [
pkgs.taplo
pkgs.cargo-nextest
];
# Environment variables
RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library";
Expand Down
5 changes: 5 additions & 0 deletions pallets/dkg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ pub mod pallet {
/// This error is returned when the ring pedersen parameters are valid
/// but the caller claims it is invalid.
ValidRingPedersenParameters,
/// The submitted Mod Proof is valid.
///
/// This error is returned when the Mod Proof is valid
/// but the caller claims it is invalid.
ValidModProof,
}

#[pallet::call]
Expand Down
97 changes: 95 additions & 2 deletions pallets/dkg/src/misbehavior/dfns_cggmp21/aux_only.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,18 @@ use sp_io::hashing::keccak_256;
use sp_runtime::DispatchResult;
use sp_std::prelude::*;
use tangle_primitives::misbehavior::{
dfns_cggmp21::{SignedRoundMessage, AUX_GEN_EID},
dfns_cggmp21::{InvalidProofReason, SignedRoundMessage, AUX_GEN_EID},
MisbehaviorSubmission,
};

use super::{zk::ring_pedersen_parameters as π_prm, DefaultDigest, Integer, SECURITY_BYTES};
use super::{
xor_array,
zk::{
no_small_factor::non_interactive as π_fac, paillier_blum_modulus as π_mod,
ring_pedersen_parameters as π_prm,
},
DefaultDigest, Integer, SECURITY_BYTES,
};

#[derive(udigest::Digestable)]
#[udigest(tag = "dfns.cggmp21.aux_gen.tag")]
Expand Down Expand Up @@ -80,6 +87,16 @@ pub struct MsgRound2 {
pub decommit: [u8; SECURITY_BYTES],
}

/// Unicast message of round 3, sent to each participant
#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub struct MsgRound3 {
/// $\psi_i$
// this should be L::M instead, but no rustc support yet
pub mod_proof: (π_mod::Commitment, π_mod::Proof),
/// $\phi_i^j$
pub fac_proof: π_fac::Proof,
}

/// Given a KeyRefresh Round1 and Round2 messages, verify the misbehavior and return the result.
pub fn invalid_decommitment<T: Config>(
data: &MisbehaviorSubmission,
Expand Down Expand Up @@ -143,3 +160,79 @@ pub fn invalid_ring_pedersen_parameters<T: Config>(
// TODO: add slashing logic
Ok(())
}

pub fn invalid_mod_proof<T: Config>(
data: &MisbehaviorSubmission,
parties_including_offender: &[[u8; 33]],
reason: &InvalidProofReason,
round2: &[SignedRoundMessage],
round3: &SignedRoundMessage,
) -> DispatchResult {
let i = round3.sender;
let n = parties_including_offender.len() as u16;
Pallet::<T>::ensure_signed_by_offender(round3, data.offender)?;
ensure!(round2.len() == usize::from(n), Error::<T>::InvalidJustification);
round2
.iter()
.zip(parties_including_offender)
.try_for_each(|(r, p)| Pallet::<T>::ensure_signed_by(r, *p))?;

let decomm = round2.get(usize::from(i)).ok_or(Error::<T>::InvalidJustification)?;
// double-check
Pallet::<T>::ensure_signed_by_offender(decomm, data.offender)?;

let job_id_bytes = data.job_id.to_be_bytes();
let mix = keccak_256(AUX_GEN_EID);
let eid_bytes = [&job_id_bytes[..], &mix[..]].concat();
let parties_shared_state = DefaultDigest::new_with_prefix(DefaultDigest::digest(eid_bytes));

let round2_msgs = round2
.iter()
.map(|r| {
postcard::from_bytes::<MsgRound2>(&r.message)
.map_err(|_| Error::<T>::MalformedRoundMessage)
})
.collect::<Result<Vec<_>, _>>()?;
let round2_msg = round2_msgs.get(usize::from(i)).ok_or(Error::<T>::InvalidJustification)?;

// rho in paper, collective random bytes
let rho_bytes = round2_msgs.iter().map(|d| &d.rho_bytes).fold([0u8; SECURITY_BYTES], xor_array);

let round3_msg = postcard::from_bytes::<MsgRound3>(&round3.message)
.map_err(|_| Error::<T>::MalformedRoundMessage)?;

let data = π_mod::Data { n: round2_msg.N.clone() };
let (commitment, proof) = &round3_msg.mod_proof;

let invalid_proof = match reason {
InvalidProofReason::ModulusIsPrime => π_mod::verify_n_is_prime(&data),
InvalidProofReason::ModulusIsEven => π_mod::verify_n_is_even(&data),
InvalidProofReason::IncorrectNthRoot(i) => π_mod::verify_incorrect_nth_root(
usize::from(*i),
parties_shared_state
.clone()
.chain_update(i.to_be_bytes())
.chain_update(rho_bytes),
&data,
proof,
commitment,
),
InvalidProofReason::IncorrectFourthRoot(i) => π_mod::verify_incorrect_fourth_root(
usize::from(*i),
parties_shared_state
.clone()
.chain_update(i.to_be_bytes())
.chain_update(rho_bytes),
&data,
proof,
commitment,
),
_ => return Err(Error::<T>::InvalidJustification.into()),
};

ensure!(!invalid_proof, Error::<T>::ValidModProof);

// Slash the offender!
// TODO: add slashing logic
Ok(())
}
15 changes: 11 additions & 4 deletions pallets/dkg/src/misbehavior/dfns_cggmp21/integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,23 @@ use scale_info::TypeInfo;
use sp_core::RuntimeDebug;
use sp_std::prelude::*;

#[cfg(not(feature = "std"))]
use ::alloc::string::{String, ToString};

#[derive(Clone, RuntimeDebug, ::serde::Deserialize, ::serde::Serialize)]
pub struct RugInteger {
radix: i32,
#[cfg(not(feature = "std"))]
value: ::alloc::string::String,
#[cfg(feature = "std")]
value: ::std::string::String,
value: String,
}

impl RugInteger {
/// Create a new `RugInteger` from a utf8 bytes and a radix.
pub fn from_utf8_and_radix(v: &[u8], radix: i32) -> Result<Self, core::str::Utf8Error> {
let value = core::str::from_utf8(v).map(|x| x.to_string())?;
Ok(Self { radix, value })
}

/// Convert `RugInteger` to a `Vec<u8>`.
pub fn to_vec(&self) -> Vec<u8> {
if self.value == "0" || self.value.is_empty() {
return Vec::new()
Expand Down
11 changes: 1 addition & 10 deletions pallets/dkg/src/misbehavior/dfns_cggmp21/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use tangle_primitives::misbehavior::{
MisbehaviorSubmission,
};

use super::{hashing_rng::HashRng, DefaultDigest, SECURITY_BYTES};
use super::{hashing_rng::HashRng, xor_array, DefaultDigest, SECURITY_BYTES};

#[derive(udigest::Digestable)]
#[udigest(tag = "dfns.cggmp21.keygen.threshold.tag")]
Expand Down Expand Up @@ -231,12 +231,3 @@ pub fn schnorr_proof<T: Config>(
// Slash the offender!
Ok(())
}

pub fn xor_array<A, B>(mut a: A, b: B) -> A
where
A: AsMut<[u8]>,
B: AsRef<[u8]>,
{
a.as_mut().iter_mut().zip(b.as_ref()).for_each(|(a_i, b_i)| *a_i ^= *b_i);
a
}
13 changes: 12 additions & 1 deletion pallets/dkg/src/misbehavior/dfns_cggmp21/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl<T: Config> Pallet<T> {
/// result
pub fn verify_dfns_cggmp21_key_refresh_misbehavior(
data: &MisbehaviorSubmission,
_participants: &[[u8; 33]],
participants: &[[u8; 33]],
_t: u16,
reason: &KeyRefreshAborted,
) -> DispatchResult {
Expand All @@ -92,6 +92,8 @@ impl<T: Config> Pallet<T> {
aux_only::invalid_decommitment::<T>(data, round1, round2),
KeyRefreshAborted::InvalidRingPedersenParameters { round2 } =>
aux_only::invalid_ring_pedersen_parameters::<T>(data, round2),
KeyRefreshAborted::InvalidModProof { round2, round3, reason } =>
aux_only::invalid_mod_proof::<T>(data, participants, reason, round2, round3),
_ => unimplemented!(),
}
}
Expand All @@ -112,3 +114,12 @@ pub fn validate_public_paillier_key_size(n: &Integer) -> bool {
use malachite_base::num::logic::traits::SignificantBits;
n.significant_bits() >= 8 * (SECURITY_BITS as u64) - 1
}

pub fn xor_array<A, B>(mut a: A, b: B) -> A
where
A: AsMut<[u8]>,
B: AsRef<[u8]>,
{
a.as_mut().iter_mut().zip(b.as_ref()).for_each(|(a_i, b_i)| *a_i ^= *b_i);
a
}
9 changes: 6 additions & 3 deletions pallets/dkg/src/misbehavior/dfns_cggmp21/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
// along with Tangle. If not, see <http://www.gnu.org/licenses/>.
#![allow(non_snake_case)]

use crate::{misbehavior::dfns_cggmp21::zk::ring_pedersen_parameters::original as π_prm, mock::*};
use crate::{
misbehavior::dfns_cggmp21::{xor_array, zk::ring_pedersen_parameters::original as π_prm},
mock::*,
};

use dfns_cggmp21::{
generic_ec::Point,
Expand Down Expand Up @@ -481,7 +484,7 @@ fn submit_keygen_schnorr_proof_verification_should_work() {
let rid = round2a_msgs
.iter()
.map(|(_, d)| &d.rid)
.fold(<SecurityLevel128 as SecurityLevel>::Rid::default(), _keygen::xor_array);
.fold(<SecurityLevel128 as SecurityLevel>::Rid::default(), xor_array);

let polynomial_sum =
round2a_msgs.iter().map(|(_, d)| &d.F).sum::<Polynomial<Point<Secp256k1>>>();
Expand Down Expand Up @@ -594,7 +597,7 @@ fn submit_keygen_invalid_schnorr_proof_verification_should_work() {
let rid = round2a_msgs
.iter()
.map(|(_, d)| &d.rid)
.fold(<SecurityLevel128 as SecurityLevel>::Rid::default(), _keygen::xor_array);
.fold(<SecurityLevel128 as SecurityLevel>::Rid::default(), xor_array);

let polynomial_sum =
round2a_msgs.iter().map(|(_, d)| &d.F).sum::<Polynomial<Point<Secp256k1>>>();
Expand Down
2 changes: 2 additions & 0 deletions pallets/dkg/src/misbehavior/dfns_cggmp21/zk/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
pub mod no_small_factor;
pub mod paillier_blum_modulus;
pub mod ring_pedersen_parameters;
Loading

0 comments on commit 7d50289

Please sign in to comment.