Skip to content

Commit

Permalink
add vss primitive (#114)
Browse files Browse the repository at this point in the history
* add vss primitive

* Modify some details

* Add files via upload

* Rename BUILD to BUILD.bazel

* Delete BUILD.bazel

* Add files via upload

* Changes poly.* and vss.*
  • Loading branch information
Cryptographer63 authored Sep 20, 2023
1 parent ea637f1 commit a4fd33a
Show file tree
Hide file tree
Showing 6 changed files with 611 additions and 0 deletions.
34 changes: 34 additions & 0 deletions yacl/crypto/primitives/vss/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
load("//bazel:yacl.bzl", "yacl_cc_library", "yacl_cc_test")

package(default_visibility = ["//visibility:public"])

yacl_cc_library(
name = "poly",
srcs = ["poly.cc"],
hdrs = ["poly.h"],
deps = [
"//yacl/math/mpint",
],
)

yacl_cc_library(
name = "vss",
srcs = ["vss.cc"],
hdrs = ["vss.h"],
deps = [
":poly",
"//yacl/crypto/base/ecc",
"//yacl/math/mpint",
],
)

yacl_cc_test(
name = "vss_test",
srcs = ["vss_test.cc"],
deps = [
":poly",
":vss",
"//yacl/crypto/base/ecc",
"//yacl/math/mpint",
],
)
105 changes: 105 additions & 0 deletions yacl/crypto/primitives/vss/poly.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include "yacl/crypto/primitives/vss/poly.h"

namespace yacl::crypto {

// Generate a random polynomial with the given zero value, threshold, and
// modulus_.
void Polynomial::CreatePolynomial(const math::MPInt& zero_value,
size_t threshold) {
// Create a vector to hold the polynomial coefficients.
std::vector<math::MPInt> coefficients(threshold);

// Set the constant term (coefficient[0]) of the polynomial to the given
// zero_value.
coefficients[0] = zero_value;

// Generate random coefficients for the remaining terms of the polynomial.
for (size_t i = 1; i < threshold; ++i) {
// Create a variable to hold the current coefficient being generated.
math::MPInt coefficient_i;

// Generate a random integer less than modulus_ and assign it to
// coefficient_i.
math::MPInt::RandomLtN(this->modulus_, &coefficient_i);

// Set the current coefficient to the generated random value.
coefficients[i] = coefficient_i;
}

// Set the generated coefficients as the coefficients of the polynomial.
SetCoeffs(coefficients);
}

// Horner's method for computing the polynomial value at a given x.
void Polynomial::EvaluatePolynomial(const math::MPInt& x,
math::MPInt& result) const {
// Initialize the result to the constant term (coefficient of highest degree)
// of the polynomial.
if (!coeffs_.empty()) {
result = coeffs_.back();
} else {
// If the coefficients vector is empty, print a warning message.
std::cout << "coeffs_ is empty!!!" << std::endl;
}

// Evaluate the polynomial using Horner's method.
// Starting from the second highest degree coefficient to the constant term
// (coefficient[0]).
for (int i = coeffs_.size() - 2; i >= 0; --i) {
// Create a duplicate of the given x to avoid modifying it.
// math::MPInt x_dup = x;

// Multiply the current result with the x value and update the result.
// result = x_dup.MulMod(result, modulus_);
result = x.MulMod(result, modulus_);
// Add the next coefficient to the result.
result = result.AddMod(coeffs_[i], modulus_);
}
}

// Lagrange Interpolation algorithm for polynomial interpolation.
void Polynomial::LagrangeInterpolation(std::vector<math::MPInt>& xs,
std::vector<math::MPInt>& ys,
math::MPInt& result) const {
// Initialize the accumulator to store the result of the interpolation.
math::MPInt acc(0);

// Loop over each element in the input points xs and interpolate the
// polynomial.
for (size_t i = 0; i < xs.size(); ++i) {
// Initialize the numerator and denominator for Lagrange interpolation.
math::MPInt num(1);
math::MPInt denum(1);

// Compute the numerator and denominator for the current interpolation
// point.
for (size_t j = 0; j < xs.size(); ++j) {
if (j != i) {
math::MPInt xj = xs[j];

// Update the numerator by multiplying it with the current xj.
num = num.MulMod(xj, modulus_);

// Compute the difference between the current xj and the current xi
// (xs[i]).
math::MPInt xj_sub_xi = xj.SubMod(xs[i], modulus_);

// Update the denominator by multiplying it with the difference.
denum = denum.MulMod(xj_sub_xi, modulus_);
}
}

// Compute the inverse of the denominator modulo the modulus_.
math::MPInt denum_inv = denum.InvertMod(modulus_);

// Compute the current interpolated value and add it to the accumulator.
acc = ys[i]
.MulMod(num, modulus_)
.MulMod(denum_inv, modulus_)
.AddMod(acc, modulus_);
}

// Store the final interpolated result in the 'result' variable.
result = acc;
}
} // namespace yacl::crypto
100 changes: 100 additions & 0 deletions yacl/crypto/primitives/vss/poly.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#pragma once

#include "yacl/math/mpint/mp_int.h"

namespace yacl::crypto {

// Polynomial class for polynomial manipulation and sharing.
class Polynomial {
public:
/**
* @brief Construct a new Polynomial object with modulus
*
* @param modulus
*/
Polynomial(math::MPInt modulus) : modulus_(modulus) {}

/**
* @brief Destroy the Polynomial object
*
*/
~Polynomial(){};

/**
* @brief Creates a random polynomial with the given zero_value, threshold,
* and modulus.
*
* @param zero_value
* @param threshold
* @param modulus
*/
void CreatePolynomial(const math::MPInt& zero_value, size_t threshold);

/**
* @brief Horner's method, also known as Horner's rule or Horner's scheme, is
* an algorithm for the efficient evaluation of polynomials. It is used to
* compute the value of a polynomial at a given point without the need for
* repeated multiplication and addition operations. The method is particularly
* useful for high-degree polynomials.
*
* The general form of a polynomial is:
*
* f(x) = a_n * x^n + a_{n-1} * x^{n-1} + ... + a_1 * x + a_0
*
* Horner's method allows us to compute the value of the polynomial f(x) at a
* specific point x_0 in a more efficient way by factoring out the common
* terms:
*
* f(x_0) = (((a_n * x_0 + a_{n-1}) * x_0 + a_{n-2}) * x_0 + ... + a_1) * x_0
* + a_0
*
* The algorithm proceeds iteratively, starting with the coefficient of the
* highest degree term, and at each step, it multiplies the current partial
* result by the input point x_0 and adds the next coefficient.
*
* The advantages of using Horner's method include reducing the number of
* multiplications and additions compared to the straightforward
*
* @param x
* @param modulus
* @param result
*/
void EvaluatePolynomial(const math::MPInt& x, math::MPInt& result) const;

/**
* @brief Performs Lagrange interpolation to interpolate the polynomial based
* on the given points.
*
* @param xs
* @param ys
* @param prime
* @param result
*/
void LagrangeInterpolation(std::vector<math::MPInt>& xs,
std::vector<math::MPInt>& ys,
math::MPInt& result) const;

/**
* @brief Sets the coefficients of the polynomial to the provided vector of
* MPInt.
*
* @param coefficients
*/
void SetCoeffs(const std::vector<math::MPInt>& coefficients) {
coeffs_ = coefficients;
}

/**
* @brief Returns the coefficients of the polynomial as a vector of MPInt.
*
* @return std::vector<math::MPInt>
*/
std::vector<math::MPInt> GetCoeffs() const { return coeffs_; }

private:
// Vector to store the coefficients of the polynomial.
std::vector<math::MPInt> coeffs_;
math::MPInt modulus_;
};

} // namespace yacl::crypto
132 changes: 132 additions & 0 deletions yacl/crypto/primitives/vss/vss.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#include "yacl/crypto/primitives/vss/vss.h"

namespace yacl::crypto {

// Generate shares for the Verifiable Secret Sharing scheme.
// Generate shares for the given secret using the provided polynomial.
std::vector<VerifiableSecretSharing::Share>
VerifiableSecretSharing::CreateShare(const math::MPInt& secret,
Polynomial& poly) {
// Create a polynomial with the secret as the constant term and random
// coefficients.
std::vector<math::MPInt> coefficients(this->GetThreshold());
poly.CreatePolynomial(secret, this->GetThreshold());

std::vector<math::MPInt> xs(this->GetTotal());
std::vector<math::MPInt> ys(this->GetTotal());

// Vector to store the generated shares (x, y) for the Verifiable Secret
// Sharing scheme.
std::vector<VerifiableSecretSharing::Share> shares;

// Generate shares by evaluating the polynomial at random points xs.
for (size_t i = 0; i < this->GetTotal(); i++) {
math::MPInt x_i;
math::MPInt::RandomLtN(this->GetPrime(), &x_i);

// EvaluatePolynomial uses Horner's method.
// Evaluate the polynomial at the point x_i to compute the share's
// y-coordinate (ys[i]).
poly.EvaluatePolynomial(x_i, ys[i]);

xs[i] = x_i;
shares.push_back({xs[i], ys[i]});
}

return shares;
}

// Generate shares with commitments for the Verifiable Secret Sharing scheme.
VerifiableSecretSharing::ShareWithCommitsResult
VerifiableSecretSharing::CreateShareWithCommits(
const math::MPInt& secret,
const std::unique_ptr<yacl::crypto::EcGroup>& ecc_group, Polynomial& poly) {
// Create a polynomial with the secret as the constant term and random
// coefficients.
poly.CreatePolynomial(secret, this->threshold_);

std::vector<math::MPInt> xs(this->total_);
std::vector<math::MPInt> ys(this->total_);
std::vector<VerifiableSecretSharing::Share> shares(this->total_);

// Generate shares by evaluating the polynomial at random points xs.
for (size_t i = 0; i < this->total_; i++) {
math::MPInt x_i;
math::MPInt::RandomLtN(this->prime_, &x_i);

poly.EvaluatePolynomial(x_i, ys[i]);
xs[i] = x_i;
shares[i] = {xs[i], ys[i]};
}

// Generate commitments for the polynomial coefficients using the elliptic
// curve group.
std::vector<yacl::crypto::EcPoint> commits =
CreateCommits(ecc_group, poly.GetCoeffs());

return std::make_pair(shares, commits);
}

// Recover the secret from the shares using Lagrange interpolation.
math::MPInt VerifiableSecretSharing::RecoverSecret(
absl::Span<const VerifiableSecretSharing::Share> shares) {
YACL_ENFORCE(shares.size() == threshold_);

math::MPInt secret(0);
std::vector<math::MPInt> xs(shares.size());
std::vector<math::MPInt> ys(shares.size());

// Extract xs and ys from the given shares.
for (size_t i = 0; i < shares.size(); i++) {
xs[i] = shares[i].x;
ys[i] = shares[i].y;
}

// Use Lagrange interpolation to recover the secret from the shares.
Polynomial poly(this->prime_);
poly.LagrangeInterpolation(xs, ys, secret);

return secret;
}

// Generate commitments for the given coefficients using the provided elliptic
// curve group.
std::vector<yacl::crypto::EcPoint> CreateCommits(
const std::unique_ptr<yacl::crypto::EcGroup>& ecc_group,
const std::vector<math::MPInt>& coefficients) {
std::vector<yacl::crypto::EcPoint> commits(coefficients.size());
for (size_t i = 0; i < coefficients.size(); i++) {
// Commit each coefficient by multiplying it with the base point of the
// group.
commits[i] = ecc_group->MulBase(coefficients[i]);
}
return commits;
}

// Verify the commitments and shares in the Verifiable Secret Sharing scheme.
bool VerifyCommits(const std::unique_ptr<yacl::crypto::EcGroup>& ecc_group,
const VerifiableSecretSharing::Share& share,
const std::vector<yacl::crypto::EcPoint>& commits,
const math::MPInt& prime) {
// Compute the expected commitment of the share.y by multiplying it with the
// base point.
yacl::crypto::EcPoint expected_gy = ecc_group->MulBase(share.y);

math::MPInt x_pow_i(1);
yacl::crypto::EcPoint gy = commits[0];

// Evaluate the Lagrange polynomial at x = share.x to compute the share.y and
// verify it.
for (size_t i = 1; i < commits.size(); i++) {
x_pow_i = x_pow_i.MulMod(share.x, prime);
gy = ecc_group->Add(gy, ecc_group->Mul(commits[i], x_pow_i));
}

// Compare the computed gy with the expected_gy to verify the commitment.
if (ecc_group->PointEqual(expected_gy, gy)) {
return true;
}
return false;
}

} // namespace yacl::crypto
Loading

0 comments on commit a4fd33a

Please sign in to comment.