This example demonstrates basic Shamir Secret Sharing, and then extends it by creating derivative secrets.
#![allow(non_snake_case)]
# #[cfg(feature = "rand")]
# {
use secp::{MaybeScalar, Scalar};
use qudoku::{
InterpolatedPointPolynomial, InterpolatedSecretPolynomial, Polynomial, SecretShare, SecretSharingPolynomial,
};
// Construct a secret sharing polynomial.
let primary_secret = MaybeScalar::from_hex(
"efe45825dcdc69bd70f09fba9930835558aebf043cfc86c1c6c6b1925c2d2035"
).unwrap();
// Sample random coefficients to construct the secret polynomial.
let mut coefficients = qudoku::random_coefficients(&mut rand::rngs::OsRng, 3);
coefficients[0] = primary_secret; // Set f(0) = secret
let secret_polynomial = SecretSharingPolynomial::new(coefficients);
// Tests the polynomial is correct.
assert_eq!(secret_polynomial.evaluate(MaybeScalar::Zero), primary_secret);
assert_ne!(secret_polynomial.evaluate(MaybeScalar::one()), primary_secret);
// Issue some shares of the secret polynomial.
let shares: Vec<SecretShare> = (1..6)
.map(MaybeScalar::from)
.map(|i| secret_polynomial.issue_share(i))
.collect();
// The shares have threshold of polynomial.degree() + 1 = 3.
assert_eq!(secret_polynomial.degree(), 2);
// Interpolate the polynomial using three or more shares.
let interpolated_polynomial = InterpolatedSecretPolynomial::new(
shares[2..5].iter().cloned().collect(),
);
// Recover the primary secret.
assert_eq!(interpolated_polynomial.evaluate(MaybeScalar::Zero), primary_secret);
/**********************************************
Now is where the qudoku custom logic starts.
**********************************************/
// Pick a provably-honest `Q` point by hashing some public input data:
let Q = qudoku::hash_to_point(b"hello world!");
// Construct a Q-ified version of the secret-sharing polynomial.
//
// Z(x) = f(x) * Q
let Z = Q * secret_polynomial;
// Pre-generate and publish some number of shares of Z(x).
let mut z_shares = vec![
Z.issue_share(Scalar::random(&mut rand::rngs::OsRng).into()),
Z.issue_share(Scalar::random(&mut rand::rngs::OsRng).into()),
];
// Now Z(x) effectively has a threshold of 1. Anyone who knows the preshares
// only needs 1 additional share to interpolate Z(x). Any shareholder can
// produce such a share.
z_shares.push(shares[4] * Q);
// We can now interpolate Z(x) and derive the secret c.
let interpolated_Z = InterpolatedPointPolynomial::new(z_shares);
assert_eq!(
interpolated_Z.evaluate(MaybeScalar::Zero),
Z.evaluate(MaybeScalar::Zero)
);
// The same primary secret + the same Q point = a consistent secret c.
let c = interpolated_Z.derive_secret(MaybeScalar::Zero);
assert_eq!(
hex::encode(&c),
"ec4f719940a443dd5377124a07e12424757db3e0976e9d206c5652ae3441c545"
);
# }