Skip to content

Commit

Permalink
secret-sharing/churp: Accept rng as parameter and add fuzzing
Browse files Browse the repository at this point in the history
  • Loading branch information
peternose committed Feb 20, 2024
1 parent 5df26d3 commit 6159296
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 23 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions secret-sharing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@ authors = ["Oasis Protocol Foundation <info@oasisprotocol.org>"]
edition = "2018"

[dependencies]

# Third party.
group = "0.13.0"
p384 = { version = "0.13.0" }
rand_core = "0.6.4"

# Fuzzing.
honggfuzz = "0.5.55"
rand = "0.8.5"

[[bin]]
name = "fuzz-vss"
path = "src/vss/fuzz/main.rs"
15 changes: 9 additions & 6 deletions secret-sharing/src/churp/dealer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! CHURP dealer.
use group::{ff::PrimeField, Group, GroupEncoding};
use rand_core::RngCore;

use crate::vss::{matrix::VerificationMatrix, polynomial::BivariatePolynomial};

Expand Down Expand Up @@ -41,14 +42,14 @@ where
}

/// Creates a new dealer with a random bivariate polynomial.
pub fn random(dx: u8, dy: u8) -> Self {
let bp = BivariatePolynomial::random(dx, dy);
pub fn random(dx: u8, dy: u8, rng: &mut impl RngCore) -> Self {
let bp = BivariatePolynomial::random(dx, dy, rng);
Self::new(bp)
}

/// Creates a new dealer with a random zero-hole bivariate polynomial.
pub fn zero_hole(dx: u8, dy: u8) -> Self {
let mut bp = BivariatePolynomial::random(dx, dy);
pub fn zero_hole(dx: u8, dy: u8, rng: &mut impl RngCore) -> Self {
let mut bp = BivariatePolynomial::random(dx, dy, rng);
bp.to_zero_hole();
Self::new(bp)
}
Expand Down Expand Up @@ -77,6 +78,8 @@ impl DealerParams for NistP384 {

#[cfg(test)]
mod tests {
use rand_core::OsRng;

use super::{BivariatePolynomial, NistP384Dealer};

#[test]
Expand All @@ -87,13 +90,13 @@ mod tests {

#[test]
fn test_random() {
let d = NistP384Dealer::random(2, 3);
let d = NistP384Dealer::random(2, 3, &mut OsRng);
assert!(!d.verification_matrix().is_zero_hole()); // Zero-hole with negligible probability.
}

#[test]
fn test_zero_hole() {
let d = NistP384Dealer::zero_hole(2, 3);
let d = NistP384Dealer::zero_hole(2, 3, &mut OsRng);
assert!(d.verification_matrix().is_zero_hole());
}
}
89 changes: 89 additions & 0 deletions secret-sharing/src/vss/fuzz/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use group::ff::PrimeField;
use honggfuzz::fuzz;
use rand::{rngs::StdRng, Rng, SeedableRng};

use secret_sharing::vss::{matrix::VerificationMatrix, polynomial::BivariatePolynomial};

fn main() {
loop {
fuzz!(|data: &[u8]| {
fuzz_bivariate_polynomial_random(data);
fuzz_bivariate_polynomial_from_seed(data);
fuzz_bivariate_polynomial_from_bytes(data);

fuzz_verification_matrix_random(data);
fuzz_verification_matrix_from_seed(data);
});
}
}

fn fuzz_bivariate_polynomial_random(data: &[u8]) {
BivariatePolynomial::<p384::Scalar>::from_bytes(data.to_vec());
}

fn fuzz_bivariate_polynomial_from_seed(data: &[u8]) {
if data.len() < 32 {
return;
}

let bp = random_bivariate_polynomial(data);
let restored = BivariatePolynomial::<p384::Scalar>::from_bytes(bp.to_bytes())
.expect("deserialization should succeed");
assert_eq!(bp, restored)
}

fn fuzz_bivariate_polynomial_from_bytes(data: &[u8]) {
if data.len() < 2 {
return;
}

let deg_x = data[0] % 5;
let deg_y = data[1] % 5;
let len = BivariatePolynomial::<p384::Scalar>::byte_size(deg_x as usize, deg_y as usize);
let size = BivariatePolynomial::<p384::Scalar>::coefficient_byte_size();

if data.len() < len {
return;
}

let mut bytes = data[..len].to_vec();
bytes[0] = deg_x;
bytes[1] = deg_y;

// Make sure all values are smaller that the modulus.
for i in (2..len).step_by(size) {
bytes[i] = 0;
}

BivariatePolynomial::<p384::Scalar>::from_bytes(bytes).expect("decoding should succeed");
}

fn fuzz_verification_matrix_random(data: &[u8]) {
VerificationMatrix::<p384::ProjectivePoint>::from_bytes(data.to_vec());
}

fn fuzz_verification_matrix_from_seed(data: &[u8]) {
if data.len() < 32 {
return;
}

let bp = random_bivariate_polynomial(data);
let vm = VerificationMatrix::<p384::ProjectivePoint>::new(&bp); // Slow.
let restored = VerificationMatrix::<p384::ProjectivePoint>::from_bytes(vm.to_bytes())
.expect("deserialization should succeed");
assert_eq!(vm, restored)
}

fn random_bivariate_polynomial<Fp>(data: &[u8]) -> BivariatePolynomial<Fp>
where
Fp: PrimeField,
{
let mut seed = [0; 32];
seed.copy_from_slice(&data[..32]);
let mut rng = StdRng::from_seed(seed);

let deg_x = rng.gen_range(0..5);
let deg_y = rng.gen_range(0..5);

BivariatePolynomial::<Fp>::random(deg_x, deg_y, &mut rng)
}
9 changes: 5 additions & 4 deletions secret-sharing/src/vss/matrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,13 @@ where
}

/// Returns the size of the byte representation of a matrix element.
fn element_byte_size() -> usize {
pub fn element_byte_size() -> usize {
// Is there a better way?
G::Repr::default().as_ref().len()
}

/// Returns the size of the byte representation of the verification matrix.
fn byte_size(rows: usize, cols: usize) -> usize {
pub fn byte_size(rows: usize, cols: usize) -> usize {
2 + rows * cols * Self::element_byte_size()
}
}
Expand All @@ -130,6 +130,7 @@ where
mod tests {
use group::Group;
use p384;
use rand_core::OsRng;

use crate::vss::matrix::VerificationMatrix;

Expand All @@ -156,13 +157,13 @@ mod tests {
}

// Random bivariate polynomial (slow).
let bp: BivariatePolynomial<p384::Scalar> = BivariatePolynomial::random(5, 10);
let bp: BivariatePolynomial<p384::Scalar> = BivariatePolynomial::random(5, 10, &mut OsRng);
let _: VerificationMatrix<p384::ProjectivePoint> = VerificationMatrix::new(&bp);
}

#[test]
fn test_serialization() {
let bp: BivariatePolynomial<p384::Scalar> = BivariatePolynomial::random(2, 3);
let bp: BivariatePolynomial<p384::Scalar> = BivariatePolynomial::random(2, 3, &mut OsRng);
let vm: VerificationMatrix<p384::ProjectivePoint> = VerificationMatrix::new(&bp);
let restored: VerificationMatrix<p384::ProjectivePoint> =
VerificationMatrix::from_bytes(vm.to_bytes()).expect("deserialization should succeed");
Expand Down
28 changes: 15 additions & 13 deletions secret-sharing/src/vss/polynomial.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use group::ff::PrimeField;
use rand_core::RngCore;

/// Bivariate polynomial over a non-binary prime field.
///
Expand Down Expand Up @@ -33,15 +34,15 @@ where
}

/// Creates a bivariate polynomial with random coefficients.
pub fn random(deg_x: u8, deg_y: u8) -> Self {
pub fn random(deg_x: u8, deg_y: u8, rng: &mut impl RngCore) -> Self {
let deg_x = deg_x as usize;
let deg_y = deg_y as usize;

let mut b = Vec::with_capacity(deg_x + 1);
for _ in 0..b.capacity() {
let mut bi = Vec::with_capacity(deg_y + 1);
for _ in 0..bi.capacity() {
let bij = Fp::random(rand_core::OsRng);
let bij = Fp::random(&mut *rng);
bi.push(bij);
}
b.push(bi);
Expand Down Expand Up @@ -125,32 +126,33 @@ where
}

/// Returns the size of the byte representation of a coefficient.
fn coefficient_byte_size() -> usize {
pub fn coefficient_byte_size() -> usize {
Fp::NUM_BITS.saturating_add(7) as usize / 8
}

/// Returns the size of the byte representation of the bivariate polynomial.
fn byte_size(deg_x: usize, deg_y: usize) -> usize {
pub fn byte_size(deg_x: usize, deg_y: usize) -> usize {
2 + (deg_x + 1) * (deg_y + 1) * Self::coefficient_byte_size()
}
}

#[cfg(test)]
mod tests {
use p384;
use rand_core::OsRng;

use super::BivariatePolynomial;

#[test]
fn test_zero() {
let bp: BivariatePolynomial<p384::Scalar> = BivariatePolynomial::zero(0, 0);
let bp = BivariatePolynomial::<p384::Scalar>::zero(0, 0);
assert_eq!(bp.deg_x, 0);
assert_eq!(bp.deg_y, 0);
assert_eq!(bp.b.len(), 1);
assert_eq!(bp.b[0].len(), 1);
assert_eq!(bp.b[0][0], p384::Scalar::ZERO);

let bp: BivariatePolynomial<p384::Scalar> = BivariatePolynomial::zero(2, 3);
let bp = BivariatePolynomial::<p384::Scalar>::zero(2, 3);
assert_eq!(bp.deg_x, 2);
assert_eq!(bp.deg_y, 3);
assert_eq!(bp.b.len(), 3);
Expand All @@ -164,14 +166,14 @@ mod tests {

#[test]
fn test_random() {
let bp: BivariatePolynomial<p384::Scalar> = BivariatePolynomial::random(0, 0);
let bp = BivariatePolynomial::<p384::Scalar>::random(0, 0, &mut OsRng);
assert_eq!(bp.deg_x, 0);
assert_eq!(bp.deg_y, 0);
assert_eq!(bp.b.len(), 1);
assert_eq!(bp.b[0].len(), 1);
assert_ne!(bp.b[0][0], p384::Scalar::ZERO); // Zero with negligible probability.

let bp: BivariatePolynomial<p384::Scalar> = BivariatePolynomial::random(2, 3);
let bp = BivariatePolynomial::<p384::Scalar>::random(2, 3, &mut OsRng);
assert_eq!(bp.deg_x, 2);
assert_eq!(bp.deg_y, 3);
assert_eq!(bp.b.len(), 3);
Expand All @@ -185,7 +187,7 @@ mod tests {

#[test]
fn test_set_coefficient() {
let mut bp: BivariatePolynomial<p384::Scalar> = BivariatePolynomial::zero(2, 3);
let mut bp = BivariatePolynomial::<p384::Scalar>::zero(2, 3);
assert_eq!(bp.b[0][0], p384::Scalar::ZERO);

bp.set_coefficient(p384::Scalar::ONE, 0, 0);
Expand All @@ -194,7 +196,7 @@ mod tests {

#[test]
fn test_to_zero_hole() {
let mut bp: BivariatePolynomial<p384::Scalar> = BivariatePolynomial::random(2, 3);
let mut bp = BivariatePolynomial::random(2, 3, &mut OsRng);

bp.set_coefficient(p384::Scalar::ONE, 0, 0);
assert_eq!(bp.b[0][0], p384::Scalar::ONE);
Expand All @@ -205,9 +207,9 @@ mod tests {

#[test]
fn test_serialization() {
let bp: BivariatePolynomial<p384::Scalar> = BivariatePolynomial::random(2, 3);
let restored: BivariatePolynomial<p384::Scalar> =
BivariatePolynomial::from_bytes(bp.to_bytes()).expect("deserialization should succeed");
let bp = BivariatePolynomial::<p384::Scalar>::random(2, 3, &mut OsRng);
let restored = BivariatePolynomial::<p384::Scalar>::from_bytes(bp.to_bytes())
.expect("deserialization should succeed");

assert_eq!(bp, restored)
}
Expand Down

0 comments on commit 6159296

Please sign in to comment.