Fix libsnark dependency build.

This changes libsnark to build in-place, instead of copying first to
a build directory. Previously, modifications made to the original
sources wouldn't get rebuilt without a 'make clean' because users
would be pointing to the copies.

This closes #2689.
This commit is contained in:
syd
2017-11-24 13:54:17 -05:00
parent 7888624f74
commit a55c186a74
119 changed files with 114 additions and 119 deletions

View File

@@ -0,0 +1,524 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "algebra/curves/alt_bn128/alt_bn128_g1.hpp"
namespace libsnark {
#ifdef PROFILE_OP_COUNTS
long long alt_bn128_G1::add_cnt = 0;
long long alt_bn128_G1::dbl_cnt = 0;
#endif
std::vector<size_t> alt_bn128_G1::wnaf_window_table;
std::vector<size_t> alt_bn128_G1::fixed_base_exp_window_table;
alt_bn128_G1 alt_bn128_G1::G1_zero;
alt_bn128_G1 alt_bn128_G1::G1_one;
alt_bn128_G1::alt_bn128_G1()
{
this->X = G1_zero.X;
this->Y = G1_zero.Y;
this->Z = G1_zero.Z;
}
void alt_bn128_G1::print() const
{
if (this->is_zero())
{
printf("O\n");
}
else
{
alt_bn128_G1 copy(*this);
copy.to_affine_coordinates();
gmp_printf("(%Nd , %Nd)\n",
copy.X.as_bigint().data, alt_bn128_Fq::num_limbs,
copy.Y.as_bigint().data, alt_bn128_Fq::num_limbs);
}
}
void alt_bn128_G1::print_coordinates() const
{
if (this->is_zero())
{
printf("O\n");
}
else
{
gmp_printf("(%Nd : %Nd : %Nd)\n",
this->X.as_bigint().data, alt_bn128_Fq::num_limbs,
this->Y.as_bigint().data, alt_bn128_Fq::num_limbs,
this->Z.as_bigint().data, alt_bn128_Fq::num_limbs);
}
}
void alt_bn128_G1::to_affine_coordinates()
{
if (this->is_zero())
{
this->X = alt_bn128_Fq::zero();
this->Y = alt_bn128_Fq::one();
this->Z = alt_bn128_Fq::zero();
}
else
{
alt_bn128_Fq Z_inv = Z.inverse();
alt_bn128_Fq Z2_inv = Z_inv.squared();
alt_bn128_Fq Z3_inv = Z2_inv * Z_inv;
this->X = this->X * Z2_inv;
this->Y = this->Y * Z3_inv;
this->Z = alt_bn128_Fq::one();
}
}
void alt_bn128_G1::to_special()
{
this->to_affine_coordinates();
}
bool alt_bn128_G1::is_special() const
{
return (this->is_zero() || this->Z == alt_bn128_Fq::one());
}
bool alt_bn128_G1::is_zero() const
{
return (this->Z.is_zero());
}
bool alt_bn128_G1::operator==(const alt_bn128_G1 &other) const
{
if (this->is_zero())
{
return other.is_zero();
}
if (other.is_zero())
{
return false;
}
/* now neither is O */
// using Jacobian coordinates so:
// (X1:Y1:Z1) = (X2:Y2:Z2)
// iff
// X1/Z1^2 == X2/Z2^2 and Y1/Z1^3 == Y2/Z2^3
// iff
// X1 * Z2^2 == X2 * Z1^2 and Y1 * Z2^3 == Y2 * Z1^3
alt_bn128_Fq Z1_squared = (this->Z).squared();
alt_bn128_Fq Z2_squared = (other.Z).squared();
if ((this->X * Z2_squared) != (other.X * Z1_squared))
{
return false;
}
alt_bn128_Fq Z1_cubed = (this->Z) * Z1_squared;
alt_bn128_Fq Z2_cubed = (other.Z) * Z2_squared;
if ((this->Y * Z2_cubed) != (other.Y * Z1_cubed))
{
return false;
}
return true;
}
bool alt_bn128_G1::operator!=(const alt_bn128_G1& other) const
{
return !(operator==(other));
}
alt_bn128_G1 alt_bn128_G1::operator+(const alt_bn128_G1 &other) const
{
// handle special cases having to do with O
if (this->is_zero())
{
return other;
}
if (other.is_zero())
{
return *this;
}
// no need to handle points of order 2,4
// (they cannot exist in a prime-order subgroup)
// check for doubling case
// using Jacobian coordinates so:
// (X1:Y1:Z1) = (X2:Y2:Z2)
// iff
// X1/Z1^2 == X2/Z2^2 and Y1/Z1^3 == Y2/Z2^3
// iff
// X1 * Z2^2 == X2 * Z1^2 and Y1 * Z2^3 == Y2 * Z1^3
alt_bn128_Fq Z1Z1 = (this->Z).squared();
alt_bn128_Fq Z2Z2 = (other.Z).squared();
alt_bn128_Fq U1 = this->X * Z2Z2;
alt_bn128_Fq U2 = other.X * Z1Z1;
alt_bn128_Fq Z1_cubed = (this->Z) * Z1Z1;
alt_bn128_Fq Z2_cubed = (other.Z) * Z2Z2;
alt_bn128_Fq S1 = (this->Y) * Z2_cubed; // S1 = Y1 * Z2 * Z2Z2
alt_bn128_Fq S2 = (other.Y) * Z1_cubed; // S2 = Y2 * Z1 * Z1Z1
if (U1 == U2 && S1 == S2)
{
// dbl case; nothing of above can be reused
return this->dbl();
}
// rest of add case
alt_bn128_Fq H = U2 - U1; // H = U2-U1
alt_bn128_Fq S2_minus_S1 = S2-S1;
alt_bn128_Fq I = (H+H).squared(); // I = (2 * H)^2
alt_bn128_Fq J = H * I; // J = H * I
alt_bn128_Fq r = S2_minus_S1 + S2_minus_S1; // r = 2 * (S2-S1)
alt_bn128_Fq V = U1 * I; // V = U1 * I
alt_bn128_Fq X3 = r.squared() - J - (V+V); // X3 = r^2 - J - 2 * V
alt_bn128_Fq S1_J = S1 * J;
alt_bn128_Fq Y3 = r * (V-X3) - (S1_J+S1_J); // Y3 = r * (V-X3)-2 S1 J
alt_bn128_Fq Z3 = ((this->Z+other.Z).squared()-Z1Z1-Z2Z2) * H; // Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2) * H
return alt_bn128_G1(X3, Y3, Z3);
}
alt_bn128_G1 alt_bn128_G1::operator-() const
{
return alt_bn128_G1(this->X, -(this->Y), this->Z);
}
alt_bn128_G1 alt_bn128_G1::operator-(const alt_bn128_G1 &other) const
{
return (*this) + (-other);
}
alt_bn128_G1 alt_bn128_G1::add(const alt_bn128_G1 &other) const
{
// handle special cases having to do with O
if (this->is_zero())
{
return other;
}
if (other.is_zero())
{
return *this;
}
// no need to handle points of order 2,4
// (they cannot exist in a prime-order subgroup)
// handle double case
if (this->operator==(other))
{
return this->dbl();
}
#ifdef PROFILE_OP_COUNTS
this->add_cnt++;
#endif
// NOTE: does not handle O and pts of order 2,4
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
alt_bn128_Fq Z1Z1 = (this->Z).squared(); // Z1Z1 = Z1^2
alt_bn128_Fq Z2Z2 = (other.Z).squared(); // Z2Z2 = Z2^2
alt_bn128_Fq U1 = (this->X) * Z2Z2; // U1 = X1 * Z2Z2
alt_bn128_Fq U2 = (other.X) * Z1Z1; // U2 = X2 * Z1Z1
alt_bn128_Fq S1 = (this->Y) * (other.Z) * Z2Z2; // S1 = Y1 * Z2 * Z2Z2
alt_bn128_Fq S2 = (other.Y) * (this->Z) * Z1Z1; // S2 = Y2 * Z1 * Z1Z1
alt_bn128_Fq H = U2 - U1; // H = U2-U1
alt_bn128_Fq S2_minus_S1 = S2-S1;
alt_bn128_Fq I = (H+H).squared(); // I = (2 * H)^2
alt_bn128_Fq J = H * I; // J = H * I
alt_bn128_Fq r = S2_minus_S1 + S2_minus_S1; // r = 2 * (S2-S1)
alt_bn128_Fq V = U1 * I; // V = U1 * I
alt_bn128_Fq X3 = r.squared() - J - (V+V); // X3 = r^2 - J - 2 * V
alt_bn128_Fq S1_J = S1 * J;
alt_bn128_Fq Y3 = r * (V-X3) - (S1_J+S1_J); // Y3 = r * (V-X3)-2 S1 J
alt_bn128_Fq Z3 = ((this->Z+other.Z).squared()-Z1Z1-Z2Z2) * H; // Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2) * H
return alt_bn128_G1(X3, Y3, Z3);
}
alt_bn128_G1 alt_bn128_G1::mixed_add(const alt_bn128_G1 &other) const
{
#ifdef DEBUG
assert(other.is_special());
#endif
// handle special cases having to do with O
if (this->is_zero())
{
return other;
}
if (other.is_zero())
{
return *this;
}
// no need to handle points of order 2,4
// (they cannot exist in a prime-order subgroup)
// check for doubling case
// using Jacobian coordinates so:
// (X1:Y1:Z1) = (X2:Y2:Z2)
// iff
// X1/Z1^2 == X2/Z2^2 and Y1/Z1^3 == Y2/Z2^3
// iff
// X1 * Z2^2 == X2 * Z1^2 and Y1 * Z2^3 == Y2 * Z1^3
// we know that Z2 = 1
const alt_bn128_Fq Z1Z1 = (this->Z).squared();
const alt_bn128_Fq &U1 = this->X;
const alt_bn128_Fq U2 = other.X * Z1Z1;
const alt_bn128_Fq Z1_cubed = (this->Z) * Z1Z1;
const alt_bn128_Fq &S1 = (this->Y); // S1 = Y1 * Z2 * Z2Z2
const alt_bn128_Fq S2 = (other.Y) * Z1_cubed; // S2 = Y2 * Z1 * Z1Z1
if (U1 == U2 && S1 == S2)
{
// dbl case; nothing of above can be reused
return this->dbl();
}
#ifdef PROFILE_OP_COUNTS
this->add_cnt++;
#endif
// NOTE: does not handle O and pts of order 2,4
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl
alt_bn128_Fq H = U2-(this->X); // H = U2-X1
alt_bn128_Fq HH = H.squared() ; // HH = H&2
alt_bn128_Fq I = HH+HH; // I = 4*HH
I = I + I;
alt_bn128_Fq J = H*I; // J = H*I
alt_bn128_Fq r = S2-(this->Y); // r = 2*(S2-Y1)
r = r + r;
alt_bn128_Fq V = (this->X) * I ; // V = X1*I
alt_bn128_Fq X3 = r.squared()-J-V-V; // X3 = r^2-J-2*V
alt_bn128_Fq Y3 = (this->Y)*J; // Y3 = r*(V-X3)-2*Y1*J
Y3 = r*(V-X3) - Y3 - Y3;
alt_bn128_Fq Z3 = ((this->Z)+H).squared() - Z1Z1 - HH; // Z3 = (Z1+H)^2-Z1Z1-HH
return alt_bn128_G1(X3, Y3, Z3);
}
alt_bn128_G1 alt_bn128_G1::dbl() const
{
#ifdef PROFILE_OP_COUNTS
this->dbl_cnt++;
#endif
// handle point at infinity
if (this->is_zero())
{
return (*this);
}
// no need to handle points of order 2,4
// (they cannot exist in a prime-order subgroup)
// NOTE: does not handle O and pts of order 2,4
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
alt_bn128_Fq A = (this->X).squared(); // A = X1^2
alt_bn128_Fq B = (this->Y).squared(); // B = Y1^2
alt_bn128_Fq C = B.squared(); // C = B^2
alt_bn128_Fq D = (this->X + B).squared() - A - C;
D = D+D; // D = 2 * ((X1 + B)^2 - A - C)
alt_bn128_Fq E = A + A + A; // E = 3 * A
alt_bn128_Fq F = E.squared(); // F = E^2
alt_bn128_Fq X3 = F - (D+D); // X3 = F - 2 D
alt_bn128_Fq eightC = C+C;
eightC = eightC + eightC;
eightC = eightC + eightC;
alt_bn128_Fq Y3 = E * (D - X3) - eightC; // Y3 = E * (D - X3) - 8 * C
alt_bn128_Fq Y1Z1 = (this->Y)*(this->Z);
alt_bn128_Fq Z3 = Y1Z1 + Y1Z1; // Z3 = 2 * Y1 * Z1
return alt_bn128_G1(X3, Y3, Z3);
}
bool alt_bn128_G1::is_well_formed() const
{
if (this->is_zero())
{
return true;
}
else
{
/*
y^2 = x^3 + b
We are using Jacobian coordinates, so equation we need to check is actually
(y/z^3)^2 = (x/z^2)^3 + b
y^2 / z^6 = x^3 / z^6 + b
y^2 = x^3 + b z^6
*/
alt_bn128_Fq X2 = this->X.squared();
alt_bn128_Fq Y2 = this->Y.squared();
alt_bn128_Fq Z2 = this->Z.squared();
alt_bn128_Fq X3 = this->X * X2;
alt_bn128_Fq Z3 = this->Z * Z2;
alt_bn128_Fq Z6 = Z3.squared();
return (Y2 == X3 + alt_bn128_coeff_b * Z6);
}
}
alt_bn128_G1 alt_bn128_G1::zero()
{
return G1_zero;
}
alt_bn128_G1 alt_bn128_G1::one()
{
return G1_one;
}
alt_bn128_G1 alt_bn128_G1::random_element()
{
return (scalar_field::random_element().as_bigint()) * G1_one;
}
std::ostream& operator<<(std::ostream &out, const alt_bn128_G1 &g)
{
alt_bn128_G1 copy(g);
copy.to_affine_coordinates();
out << (copy.is_zero() ? 1 : 0) << OUTPUT_SEPARATOR;
#ifdef NO_PT_COMPRESSION
out << copy.X << OUTPUT_SEPARATOR << copy.Y;
#else
/* storing LSB of Y */
out << copy.X << OUTPUT_SEPARATOR << (copy.Y.as_bigint().data[0] & 1);
#endif
return out;
}
std::istream& operator>>(std::istream &in, alt_bn128_G1 &g)
{
char is_zero;
alt_bn128_Fq tX, tY;
#ifdef NO_PT_COMPRESSION
in >> is_zero >> tX >> tY;
is_zero -= '0';
#else
in.read((char*)&is_zero, 1); // this reads is_zero;
is_zero -= '0';
consume_OUTPUT_SEPARATOR(in);
unsigned char Y_lsb;
in >> tX;
consume_OUTPUT_SEPARATOR(in);
in.read((char*)&Y_lsb, 1);
Y_lsb -= '0';
// y = +/- sqrt(x^3 + b)
if (!is_zero)
{
alt_bn128_Fq tX2 = tX.squared();
alt_bn128_Fq tY2 = tX2*tX + alt_bn128_coeff_b;
tY = tY2.sqrt();
if ((tY.as_bigint().data[0] & 1) != Y_lsb)
{
tY = -tY;
}
}
#endif
// using Jacobian coordinates
if (!is_zero)
{
g.X = tX;
g.Y = tY;
g.Z = alt_bn128_Fq::one();
}
else
{
g = alt_bn128_G1::zero();
}
return in;
}
std::ostream& operator<<(std::ostream& out, const std::vector<alt_bn128_G1> &v)
{
out << v.size() << "\n";
for (const alt_bn128_G1& t : v)
{
out << t << OUTPUT_NEWLINE;
}
return out;
}
std::istream& operator>>(std::istream& in, std::vector<alt_bn128_G1> &v)
{
v.clear();
size_t s;
in >> s;
consume_newline(in);
v.reserve(s);
for (size_t i = 0; i < s; ++i)
{
alt_bn128_G1 g;
in >> g;
consume_OUTPUT_NEWLINE(in);
v.emplace_back(g);
}
return in;
}
template<>
void batch_to_special_all_non_zeros<alt_bn128_G1>(std::vector<alt_bn128_G1> &vec)
{
std::vector<alt_bn128_Fq> Z_vec;
Z_vec.reserve(vec.size());
for (auto &el: vec)
{
Z_vec.emplace_back(el.Z);
}
batch_invert<alt_bn128_Fq>(Z_vec);
const alt_bn128_Fq one = alt_bn128_Fq::one();
for (size_t i = 0; i < vec.size(); ++i)
{
alt_bn128_Fq Z2 = Z_vec[i].squared();
alt_bn128_Fq Z3 = Z_vec[i] * Z2;
vec[i].X = vec[i].X * Z2;
vec[i].Y = vec[i].Y * Z3;
vec[i].Z = one;
}
}
} // libsnark

View File

@@ -0,0 +1,95 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef ALT_BN128_G1_HPP_
#define ALT_BN128_G1_HPP_
#include <vector>
#include "algebra/curves/alt_bn128/alt_bn128_init.hpp"
#include "algebra/curves/curve_utils.hpp"
namespace libsnark {
class alt_bn128_G1;
std::ostream& operator<<(std::ostream &, const alt_bn128_G1&);
std::istream& operator>>(std::istream &, alt_bn128_G1&);
class alt_bn128_G1 {
public:
#ifdef PROFILE_OP_COUNTS
static long long add_cnt;
static long long dbl_cnt;
#endif
static std::vector<size_t> wnaf_window_table;
static std::vector<size_t> fixed_base_exp_window_table;
static alt_bn128_G1 G1_zero;
static alt_bn128_G1 G1_one;
typedef alt_bn128_Fq base_field;
typedef alt_bn128_Fr scalar_field;
alt_bn128_Fq X, Y, Z;
// using Jacobian coordinates
alt_bn128_G1();
alt_bn128_G1(const alt_bn128_Fq& X, const alt_bn128_Fq& Y, const alt_bn128_Fq& Z) : X(X), Y(Y), Z(Z) {};
void print() const;
void print_coordinates() const;
void to_affine_coordinates();
void to_special();
bool is_special() const;
bool is_zero() const;
bool operator==(const alt_bn128_G1 &other) const;
bool operator!=(const alt_bn128_G1 &other) const;
alt_bn128_G1 operator+(const alt_bn128_G1 &other) const;
alt_bn128_G1 operator-() const;
alt_bn128_G1 operator-(const alt_bn128_G1 &other) const;
alt_bn128_G1 add(const alt_bn128_G1 &other) const;
alt_bn128_G1 mixed_add(const alt_bn128_G1 &other) const;
alt_bn128_G1 dbl() const;
bool is_well_formed() const;
static alt_bn128_G1 zero();
static alt_bn128_G1 one();
static alt_bn128_G1 random_element();
static size_t size_in_bits() { return base_field::size_in_bits() + 1; }
static bigint<base_field::num_limbs> base_field_char() { return base_field::field_char(); }
static bigint<scalar_field::num_limbs> order() { return scalar_field::field_char(); }
friend std::ostream& operator<<(std::ostream &out, const alt_bn128_G1 &g);
friend std::istream& operator>>(std::istream &in, alt_bn128_G1 &g);
};
template<mp_size_t m>
alt_bn128_G1 operator*(const bigint<m> &lhs, const alt_bn128_G1 &rhs)
{
return scalar_mul<alt_bn128_G1, m>(rhs, lhs);
}
template<mp_size_t m, const bigint<m>& modulus_p>
alt_bn128_G1 operator*(const Fp_model<m,modulus_p> &lhs, const alt_bn128_G1 &rhs)
{
return scalar_mul<alt_bn128_G1, m>(rhs, lhs.as_bigint());
}
std::ostream& operator<<(std::ostream& out, const std::vector<alt_bn128_G1> &v);
std::istream& operator>>(std::istream& in, std::vector<alt_bn128_G1> &v);
template<typename T>
void batch_to_special_all_non_zeros(std::vector<T> &vec);
template<>
void batch_to_special_all_non_zeros<alt_bn128_G1>(std::vector<alt_bn128_G1> &vec);
} // libsnark
#endif // ALT_BN128_G1_HPP_

View File

@@ -0,0 +1,505 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "algebra/curves/alt_bn128/alt_bn128_g2.hpp"
namespace libsnark {
#ifdef PROFILE_OP_COUNTS
long long alt_bn128_G2::add_cnt = 0;
long long alt_bn128_G2::dbl_cnt = 0;
#endif
std::vector<size_t> alt_bn128_G2::wnaf_window_table;
std::vector<size_t> alt_bn128_G2::fixed_base_exp_window_table;
alt_bn128_G2 alt_bn128_G2::G2_zero;
alt_bn128_G2 alt_bn128_G2::G2_one;
alt_bn128_G2::alt_bn128_G2()
{
this->X = G2_zero.X;
this->Y = G2_zero.Y;
this->Z = G2_zero.Z;
}
alt_bn128_Fq2 alt_bn128_G2::mul_by_b(const alt_bn128_Fq2 &elt)
{
return alt_bn128_Fq2(alt_bn128_twist_mul_by_b_c0 * elt.c0, alt_bn128_twist_mul_by_b_c1 * elt.c1);
}
void alt_bn128_G2::print() const
{
if (this->is_zero())
{
printf("O\n");
}
else
{
alt_bn128_G2 copy(*this);
copy.to_affine_coordinates();
gmp_printf("(%Nd*z + %Nd , %Nd*z + %Nd)\n",
copy.X.c1.as_bigint().data, alt_bn128_Fq::num_limbs,
copy.X.c0.as_bigint().data, alt_bn128_Fq::num_limbs,
copy.Y.c1.as_bigint().data, alt_bn128_Fq::num_limbs,
copy.Y.c0.as_bigint().data, alt_bn128_Fq::num_limbs);
}
}
void alt_bn128_G2::print_coordinates() const
{
if (this->is_zero())
{
printf("O\n");
}
else
{
gmp_printf("(%Nd*z + %Nd : %Nd*z + %Nd : %Nd*z + %Nd)\n",
this->X.c1.as_bigint().data, alt_bn128_Fq::num_limbs,
this->X.c0.as_bigint().data, alt_bn128_Fq::num_limbs,
this->Y.c1.as_bigint().data, alt_bn128_Fq::num_limbs,
this->Y.c0.as_bigint().data, alt_bn128_Fq::num_limbs,
this->Z.c1.as_bigint().data, alt_bn128_Fq::num_limbs,
this->Z.c0.as_bigint().data, alt_bn128_Fq::num_limbs);
}
}
void alt_bn128_G2::to_affine_coordinates()
{
if (this->is_zero())
{
this->X = alt_bn128_Fq2::zero();
this->Y = alt_bn128_Fq2::one();
this->Z = alt_bn128_Fq2::zero();
}
else
{
alt_bn128_Fq2 Z_inv = Z.inverse();
alt_bn128_Fq2 Z2_inv = Z_inv.squared();
alt_bn128_Fq2 Z3_inv = Z2_inv * Z_inv;
this->X = this->X * Z2_inv;
this->Y = this->Y * Z3_inv;
this->Z = alt_bn128_Fq2::one();
}
}
void alt_bn128_G2::to_special()
{
this->to_affine_coordinates();
}
bool alt_bn128_G2::is_special() const
{
return (this->is_zero() || this->Z == alt_bn128_Fq2::one());
}
bool alt_bn128_G2::is_zero() const
{
return (this->Z.is_zero());
}
bool alt_bn128_G2::operator==(const alt_bn128_G2 &other) const
{
if (this->is_zero())
{
return other.is_zero();
}
if (other.is_zero())
{
return false;
}
/* now neither is O */
// using Jacobian coordinates so:
// (X1:Y1:Z1) = (X2:Y2:Z2)
// iff
// X1/Z1^2 == X2/Z2^2 and Y1/Z1^3 == Y2/Z2^3
// iff
// X1 * Z2^2 == X2 * Z1^2 and Y1 * Z2^3 == Y2 * Z1^3
alt_bn128_Fq2 Z1_squared = (this->Z).squared();
alt_bn128_Fq2 Z2_squared = (other.Z).squared();
if ((this->X * Z2_squared) != (other.X * Z1_squared))
{
return false;
}
alt_bn128_Fq2 Z1_cubed = (this->Z) * Z1_squared;
alt_bn128_Fq2 Z2_cubed = (other.Z) * Z2_squared;
if ((this->Y * Z2_cubed) != (other.Y * Z1_cubed))
{
return false;
}
return true;
}
bool alt_bn128_G2::operator!=(const alt_bn128_G2& other) const
{
return !(operator==(other));
}
alt_bn128_G2 alt_bn128_G2::operator+(const alt_bn128_G2 &other) const
{
// handle special cases having to do with O
if (this->is_zero())
{
return other;
}
if (other.is_zero())
{
return *this;
}
// no need to handle points of order 2,4
// (they cannot exist in a prime-order subgroup)
// check for doubling case
// using Jacobian coordinates so:
// (X1:Y1:Z1) = (X2:Y2:Z2)
// iff
// X1/Z1^2 == X2/Z2^2 and Y1/Z1^3 == Y2/Z2^3
// iff
// X1 * Z2^2 == X2 * Z1^2 and Y1 * Z2^3 == Y2 * Z1^3
alt_bn128_Fq2 Z1Z1 = (this->Z).squared();
alt_bn128_Fq2 Z2Z2 = (other.Z).squared();
alt_bn128_Fq2 U1 = this->X * Z2Z2;
alt_bn128_Fq2 U2 = other.X * Z1Z1;
alt_bn128_Fq2 Z1_cubed = (this->Z) * Z1Z1;
alt_bn128_Fq2 Z2_cubed = (other.Z) * Z2Z2;
alt_bn128_Fq2 S1 = (this->Y) * Z2_cubed; // S1 = Y1 * Z2 * Z2Z2
alt_bn128_Fq2 S2 = (other.Y) * Z1_cubed; // S2 = Y2 * Z1 * Z1Z1
if (U1 == U2 && S1 == S2)
{
// dbl case; nothing of above can be reused
return this->dbl();
}
// rest of add case
alt_bn128_Fq2 H = U2 - U1; // H = U2-U1
alt_bn128_Fq2 S2_minus_S1 = S2-S1;
alt_bn128_Fq2 I = (H+H).squared(); // I = (2 * H)^2
alt_bn128_Fq2 J = H * I; // J = H * I
alt_bn128_Fq2 r = S2_minus_S1 + S2_minus_S1; // r = 2 * (S2-S1)
alt_bn128_Fq2 V = U1 * I; // V = U1 * I
alt_bn128_Fq2 X3 = r.squared() - J - (V+V); // X3 = r^2 - J - 2 * V
alt_bn128_Fq2 S1_J = S1 * J;
alt_bn128_Fq2 Y3 = r * (V-X3) - (S1_J+S1_J); // Y3 = r * (V-X3)-2 S1 J
alt_bn128_Fq2 Z3 = ((this->Z+other.Z).squared()-Z1Z1-Z2Z2) * H; // Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2) * H
return alt_bn128_G2(X3, Y3, Z3);
}
alt_bn128_G2 alt_bn128_G2::operator-() const
{
return alt_bn128_G2(this->X, -(this->Y), this->Z);
}
alt_bn128_G2 alt_bn128_G2::operator-(const alt_bn128_G2 &other) const
{
return (*this) + (-other);
}
alt_bn128_G2 alt_bn128_G2::add(const alt_bn128_G2 &other) const
{
// handle special cases having to do with O
if (this->is_zero())
{
return other;
}
if (other.is_zero())
{
return *this;
}
// no need to handle points of order 2,4
// (they cannot exist in a prime-order subgroup)
// handle double case
if (this->operator==(other))
{
return this->dbl();
}
#ifdef PROFILE_OP_COUNTS
this->add_cnt++;
#endif
// NOTE: does not handle O and pts of order 2,4
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-projective.html#addition-add-1998-cmo-2
alt_bn128_Fq2 Z1Z1 = (this->Z).squared(); // Z1Z1 = Z1^2
alt_bn128_Fq2 Z2Z2 = (other.Z).squared(); // Z2Z2 = Z2^2
alt_bn128_Fq2 U1 = (this->X) * Z2Z2; // U1 = X1 * Z2Z2
alt_bn128_Fq2 U2 = (other.X) * Z1Z1; // U2 = X2 * Z1Z1
alt_bn128_Fq2 S1 = (this->Y) * (other.Z) * Z2Z2; // S1 = Y1 * Z2 * Z2Z2
alt_bn128_Fq2 S2 = (other.Y) * (this->Z) * Z1Z1; // S2 = Y2 * Z1 * Z1Z1
alt_bn128_Fq2 H = U2 - U1; // H = U2-U1
alt_bn128_Fq2 S2_minus_S1 = S2-S1;
alt_bn128_Fq2 I = (H+H).squared(); // I = (2 * H)^2
alt_bn128_Fq2 J = H * I; // J = H * I
alt_bn128_Fq2 r = S2_minus_S1 + S2_minus_S1; // r = 2 * (S2-S1)
alt_bn128_Fq2 V = U1 * I; // V = U1 * I
alt_bn128_Fq2 X3 = r.squared() - J - (V+V); // X3 = r^2 - J - 2 * V
alt_bn128_Fq2 S1_J = S1 * J;
alt_bn128_Fq2 Y3 = r * (V-X3) - (S1_J+S1_J); // Y3 = r * (V-X3)-2 S1 J
alt_bn128_Fq2 Z3 = ((this->Z+other.Z).squared()-Z1Z1-Z2Z2) * H; // Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2) * H
return alt_bn128_G2(X3, Y3, Z3);
}
alt_bn128_G2 alt_bn128_G2::mixed_add(const alt_bn128_G2 &other) const
{
#ifdef DEBUG
assert(other.is_special());
#endif
// handle special cases having to do with O
if (this->is_zero())
{
return other;
}
if (other.is_zero())
{
return *this;
}
// no need to handle points of order 2,4
// (they cannot exist in a prime-order subgroup)
// check for doubling case
// using Jacobian coordinates so:
// (X1:Y1:Z1) = (X2:Y2:Z2)
// iff
// X1/Z1^2 == X2/Z2^2 and Y1/Z1^3 == Y2/Z2^3
// iff
// X1 * Z2^2 == X2 * Z1^2 and Y1 * Z2^3 == Y2 * Z1^3
// we know that Z2 = 1
const alt_bn128_Fq2 Z1Z1 = (this->Z).squared();
const alt_bn128_Fq2 &U1 = this->X;
const alt_bn128_Fq2 U2 = other.X * Z1Z1;
const alt_bn128_Fq2 Z1_cubed = (this->Z) * Z1Z1;
const alt_bn128_Fq2 &S1 = (this->Y); // S1 = Y1 * Z2 * Z2Z2
const alt_bn128_Fq2 S2 = (other.Y) * Z1_cubed; // S2 = Y2 * Z1 * Z1Z1
if (U1 == U2 && S1 == S2)
{
// dbl case; nothing of above can be reused
return this->dbl();
}
#ifdef PROFILE_OP_COUNTS
this->add_cnt++;
#endif
// NOTE: does not handle O and pts of order 2,4
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl
alt_bn128_Fq2 H = U2-(this->X); // H = U2-X1
alt_bn128_Fq2 HH = H.squared() ; // HH = H&2
alt_bn128_Fq2 I = HH+HH; // I = 4*HH
I = I + I;
alt_bn128_Fq2 J = H*I; // J = H*I
alt_bn128_Fq2 r = S2-(this->Y); // r = 2*(S2-Y1)
r = r + r;
alt_bn128_Fq2 V = (this->X) * I ; // V = X1*I
alt_bn128_Fq2 X3 = r.squared()-J-V-V; // X3 = r^2-J-2*V
alt_bn128_Fq2 Y3 = (this->Y)*J; // Y3 = r*(V-X3)-2*Y1*J
Y3 = r*(V-X3) - Y3 - Y3;
alt_bn128_Fq2 Z3 = ((this->Z)+H).squared() - Z1Z1 - HH; // Z3 = (Z1+H)^2-Z1Z1-HH
return alt_bn128_G2(X3, Y3, Z3);
}
alt_bn128_G2 alt_bn128_G2::dbl() const
{
#ifdef PROFILE_OP_COUNTS
this->dbl_cnt++;
#endif
// handle point at infinity
if (this->is_zero())
{
return (*this);
}
// NOTE: does not handle O and pts of order 2,4
// http://www.hyperelliptic.org/EFD/g1p/auto-shortw-projective.html#doubling-dbl-2007-bl
alt_bn128_Fq2 A = (this->X).squared(); // A = X1^2
alt_bn128_Fq2 B = (this->Y).squared(); // B = Y1^2
alt_bn128_Fq2 C = B.squared(); // C = B^2
alt_bn128_Fq2 D = (this->X + B).squared() - A - C;
D = D+D; // D = 2 * ((X1 + B)^2 - A - C)
alt_bn128_Fq2 E = A + A + A; // E = 3 * A
alt_bn128_Fq2 F = E.squared(); // F = E^2
alt_bn128_Fq2 X3 = F - (D+D); // X3 = F - 2 D
alt_bn128_Fq2 eightC = C+C;
eightC = eightC + eightC;
eightC = eightC + eightC;
alt_bn128_Fq2 Y3 = E * (D - X3) - eightC; // Y3 = E * (D - X3) - 8 * C
alt_bn128_Fq2 Y1Z1 = (this->Y)*(this->Z);
alt_bn128_Fq2 Z3 = Y1Z1 + Y1Z1; // Z3 = 2 * Y1 * Z1
return alt_bn128_G2(X3, Y3, Z3);
}
alt_bn128_G2 alt_bn128_G2::mul_by_q() const
{
return alt_bn128_G2(alt_bn128_twist_mul_by_q_X * (this->X).Frobenius_map(1),
alt_bn128_twist_mul_by_q_Y * (this->Y).Frobenius_map(1),
(this->Z).Frobenius_map(1));
}
bool alt_bn128_G2::is_well_formed() const
{
if (this->is_zero())
{
return true;
}
else
{
/*
y^2 = x^3 + b
We are using Jacobian coordinates, so equation we need to check is actually
(y/z^3)^2 = (x/z^2)^3 + b
y^2 / z^6 = x^3 / z^6 + b
y^2 = x^3 + b z^6
*/
alt_bn128_Fq2 X2 = this->X.squared();
alt_bn128_Fq2 Y2 = this->Y.squared();
alt_bn128_Fq2 Z2 = this->Z.squared();
alt_bn128_Fq2 X3 = this->X * X2;
alt_bn128_Fq2 Z3 = this->Z * Z2;
alt_bn128_Fq2 Z6 = Z3.squared();
return (Y2 == X3 + alt_bn128_twist_coeff_b * Z6);
}
}
alt_bn128_G2 alt_bn128_G2::zero()
{
return G2_zero;
}
alt_bn128_G2 alt_bn128_G2::one()
{
return G2_one;
}
alt_bn128_G2 alt_bn128_G2::random_element()
{
return (alt_bn128_Fr::random_element().as_bigint()) * G2_one;
}
std::ostream& operator<<(std::ostream &out, const alt_bn128_G2 &g)
{
alt_bn128_G2 copy(g);
copy.to_affine_coordinates();
out << (copy.is_zero() ? 1 : 0) << OUTPUT_SEPARATOR;
#ifdef NO_PT_COMPRESSION
out << copy.X << OUTPUT_SEPARATOR << copy.Y;
#else
/* storing LSB of Y */
out << copy.X << OUTPUT_SEPARATOR << (copy.Y.c0.as_bigint().data[0] & 1);
#endif
return out;
}
std::istream& operator>>(std::istream &in, alt_bn128_G2 &g)
{
char is_zero;
alt_bn128_Fq2 tX, tY;
#ifdef NO_PT_COMPRESSION
in >> is_zero >> tX >> tY;
is_zero -= '0';
#else
in.read((char*)&is_zero, 1); // this reads is_zero;
is_zero -= '0';
consume_OUTPUT_SEPARATOR(in);
unsigned char Y_lsb;
in >> tX;
consume_OUTPUT_SEPARATOR(in);
in.read((char*)&Y_lsb, 1);
Y_lsb -= '0';
// y = +/- sqrt(x^3 + b)
if (!is_zero)
{
alt_bn128_Fq2 tX2 = tX.squared();
alt_bn128_Fq2 tY2 = tX2 * tX + alt_bn128_twist_coeff_b;
tY = tY2.sqrt();
if ((tY.c0.as_bigint().data[0] & 1) != Y_lsb)
{
tY = -tY;
}
}
#endif
// using projective coordinates
if (!is_zero)
{
g.X = tX;
g.Y = tY;
g.Z = alt_bn128_Fq2::one();
}
else
{
g = alt_bn128_G2::zero();
}
return in;
}
template<>
void batch_to_special_all_non_zeros<alt_bn128_G2>(std::vector<alt_bn128_G2> &vec)
{
std::vector<alt_bn128_Fq2> Z_vec;
Z_vec.reserve(vec.size());
for (auto &el: vec)
{
Z_vec.emplace_back(el.Z);
}
batch_invert<alt_bn128_Fq2>(Z_vec);
const alt_bn128_Fq2 one = alt_bn128_Fq2::one();
for (size_t i = 0; i < vec.size(); ++i)
{
alt_bn128_Fq2 Z2 = Z_vec[i].squared();
alt_bn128_Fq2 Z3 = Z_vec[i] * Z2;
vec[i].X = vec[i].X * Z2;
vec[i].Y = vec[i].Y * Z3;
vec[i].Z = one;
}
}
} // libsnark

View File

@@ -0,0 +1,96 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef ALT_BN128_G2_HPP_
#define ALT_BN128_G2_HPP_
#include <vector>
#include "algebra/curves/alt_bn128/alt_bn128_init.hpp"
#include "algebra/curves/curve_utils.hpp"
namespace libsnark {
class alt_bn128_G2;
std::ostream& operator<<(std::ostream &, const alt_bn128_G2&);
std::istream& operator>>(std::istream &, alt_bn128_G2&);
class alt_bn128_G2 {
public:
#ifdef PROFILE_OP_COUNTS
static long long add_cnt;
static long long dbl_cnt;
#endif
static std::vector<size_t> wnaf_window_table;
static std::vector<size_t> fixed_base_exp_window_table;
static alt_bn128_G2 G2_zero;
static alt_bn128_G2 G2_one;
typedef alt_bn128_Fq base_field;
typedef alt_bn128_Fq2 twist_field;
typedef alt_bn128_Fr scalar_field;
alt_bn128_Fq2 X, Y, Z;
// using Jacobian coordinates
alt_bn128_G2();
alt_bn128_G2(const alt_bn128_Fq2& X, const alt_bn128_Fq2& Y, const alt_bn128_Fq2& Z) : X(X), Y(Y), Z(Z) {};
static alt_bn128_Fq2 mul_by_b(const alt_bn128_Fq2 &elt);
void print() const;
void print_coordinates() const;
void to_affine_coordinates();
void to_special();
bool is_special() const;
bool is_zero() const;
bool operator==(const alt_bn128_G2 &other) const;
bool operator!=(const alt_bn128_G2 &other) const;
alt_bn128_G2 operator+(const alt_bn128_G2 &other) const;
alt_bn128_G2 operator-() const;
alt_bn128_G2 operator-(const alt_bn128_G2 &other) const;
alt_bn128_G2 add(const alt_bn128_G2 &other) const;
alt_bn128_G2 mixed_add(const alt_bn128_G2 &other) const;
alt_bn128_G2 dbl() const;
alt_bn128_G2 mul_by_q() const;
bool is_well_formed() const;
static alt_bn128_G2 zero();
static alt_bn128_G2 one();
static alt_bn128_G2 random_element();
static size_t size_in_bits() { return twist_field::size_in_bits() + 1; }
static bigint<base_field::num_limbs> base_field_char() { return base_field::field_char(); }
static bigint<scalar_field::num_limbs> order() { return scalar_field::field_char(); }
friend std::ostream& operator<<(std::ostream &out, const alt_bn128_G2 &g);
friend std::istream& operator>>(std::istream &in, alt_bn128_G2 &g);
};
template<mp_size_t m>
alt_bn128_G2 operator*(const bigint<m> &lhs, const alt_bn128_G2 &rhs)
{
return scalar_mul<alt_bn128_G2, m>(rhs, lhs);
}
template<mp_size_t m, const bigint<m>& modulus_p>
alt_bn128_G2 operator*(const Fp_model<m,modulus_p> &lhs, const alt_bn128_G2 &rhs)
{
return scalar_mul<alt_bn128_G2, m>(rhs, lhs.as_bigint());
}
template<typename T>
void batch_to_special_all_non_zeros(std::vector<T> &vec);
template<>
void batch_to_special_all_non_zeros<alt_bn128_G2>(std::vector<alt_bn128_G2> &vec);
} // libsnark
#endif // ALT_BN128_G2_HPP_

View File

@@ -0,0 +1,273 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "algebra/curves/alt_bn128/alt_bn128_init.hpp"
#include "algebra/curves/alt_bn128/alt_bn128_g1.hpp"
#include "algebra/curves/alt_bn128/alt_bn128_g2.hpp"
namespace libsnark {
bigint<alt_bn128_r_limbs> alt_bn128_modulus_r;
bigint<alt_bn128_q_limbs> alt_bn128_modulus_q;
alt_bn128_Fq alt_bn128_coeff_b;
alt_bn128_Fq2 alt_bn128_twist;
alt_bn128_Fq2 alt_bn128_twist_coeff_b;
alt_bn128_Fq alt_bn128_twist_mul_by_b_c0;
alt_bn128_Fq alt_bn128_twist_mul_by_b_c1;
alt_bn128_Fq2 alt_bn128_twist_mul_by_q_X;
alt_bn128_Fq2 alt_bn128_twist_mul_by_q_Y;
bigint<alt_bn128_q_limbs> alt_bn128_ate_loop_count;
bool alt_bn128_ate_is_loop_count_neg;
bigint<12*alt_bn128_q_limbs> alt_bn128_final_exponent;
bigint<alt_bn128_q_limbs> alt_bn128_final_exponent_z;
bool alt_bn128_final_exponent_is_z_neg;
void init_alt_bn128_params()
{
typedef bigint<alt_bn128_r_limbs> bigint_r;
typedef bigint<alt_bn128_q_limbs> bigint_q;
assert(sizeof(mp_limb_t) == 8 || sizeof(mp_limb_t) == 4); // Montgomery assumes this
/* parameters for scalar field Fr */
alt_bn128_modulus_r = bigint_r("21888242871839275222246405745257275088548364400416034343698204186575808495617");
assert(alt_bn128_Fr::modulus_is_valid());
if (sizeof(mp_limb_t) == 8)
{
alt_bn128_Fr::Rsquared = bigint_r("944936681149208446651664254269745548490766851729442924617792859073125903783");
alt_bn128_Fr::Rcubed = bigint_r("5866548545943845227489894872040244720403868105578784105281690076696998248512");
alt_bn128_Fr::inv = 0xc2e1f593efffffff;
}
if (sizeof(mp_limb_t) == 4)
{
alt_bn128_Fr::Rsquared = bigint_r("944936681149208446651664254269745548490766851729442924617792859073125903783");
alt_bn128_Fr::Rcubed = bigint_r("5866548545943845227489894872040244720403868105578784105281690076696998248512");
alt_bn128_Fr::inv = 0xefffffff;
}
alt_bn128_Fr::num_bits = 254;
alt_bn128_Fr::euler = bigint_r("10944121435919637611123202872628637544274182200208017171849102093287904247808");
alt_bn128_Fr::s = 28;
alt_bn128_Fr::t = bigint_r("81540058820840996586704275553141814055101440848469862132140264610111");
alt_bn128_Fr::t_minus_1_over_2 = bigint_r("40770029410420498293352137776570907027550720424234931066070132305055");
alt_bn128_Fr::multiplicative_generator = alt_bn128_Fr("5");
alt_bn128_Fr::root_of_unity = alt_bn128_Fr("19103219067921713944291392827692070036145651957329286315305642004821462161904");
alt_bn128_Fr::nqr = alt_bn128_Fr("5");
alt_bn128_Fr::nqr_to_t = alt_bn128_Fr("19103219067921713944291392827692070036145651957329286315305642004821462161904");
/* parameters for base field Fq */
alt_bn128_modulus_q = bigint_q("21888242871839275222246405745257275088696311157297823662689037894645226208583");
assert(alt_bn128_Fq::modulus_is_valid());
if (sizeof(mp_limb_t) == 8)
{
alt_bn128_Fq::Rsquared = bigint_q("3096616502983703923843567936837374451735540968419076528771170197431451843209");
alt_bn128_Fq::Rcubed = bigint_q("14921786541159648185948152738563080959093619838510245177710943249661917737183");
alt_bn128_Fq::inv = 0x87d20782e4866389;
}
if (sizeof(mp_limb_t) == 4)
{
alt_bn128_Fq::Rsquared = bigint_q("3096616502983703923843567936837374451735540968419076528771170197431451843209");
alt_bn128_Fq::Rcubed = bigint_q("14921786541159648185948152738563080959093619838510245177710943249661917737183");
alt_bn128_Fq::inv = 0xe4866389;
}
alt_bn128_Fq::num_bits = 254;
alt_bn128_Fq::euler = bigint_q("10944121435919637611123202872628637544348155578648911831344518947322613104291");
alt_bn128_Fq::s = 1;
alt_bn128_Fq::t = bigint_q("10944121435919637611123202872628637544348155578648911831344518947322613104291");
alt_bn128_Fq::t_minus_1_over_2 = bigint_q("5472060717959818805561601436314318772174077789324455915672259473661306552145");
alt_bn128_Fq::multiplicative_generator = alt_bn128_Fq("3");
alt_bn128_Fq::root_of_unity = alt_bn128_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582");
alt_bn128_Fq::nqr = alt_bn128_Fq("3");
alt_bn128_Fq::nqr_to_t = alt_bn128_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582");
/* parameters for twist field Fq2 */
alt_bn128_Fq2::euler = bigint<2*alt_bn128_q_limbs>("239547588008311421220994022608339370399626158265550411218223901127035046843189118723920525909718935985594116157406550130918127817069793474323196511433944");
alt_bn128_Fq2::s = 4;
alt_bn128_Fq2::t = bigint<2*alt_bn128_q_limbs>("29943448501038927652624252826042421299953269783193801402277987640879380855398639840490065738714866998199264519675818766364765977133724184290399563929243");
alt_bn128_Fq2::t_minus_1_over_2 = bigint<2*alt_bn128_q_limbs>("14971724250519463826312126413021210649976634891596900701138993820439690427699319920245032869357433499099632259837909383182382988566862092145199781964621");
alt_bn128_Fq2::non_residue = alt_bn128_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582");
alt_bn128_Fq2::nqr = alt_bn128_Fq2(alt_bn128_Fq("2"),alt_bn128_Fq("1"));
alt_bn128_Fq2::nqr_to_t = alt_bn128_Fq2(alt_bn128_Fq("5033503716262624267312492558379982687175200734934877598599011485707452665730"),alt_bn128_Fq("314498342015008975724433667930697407966947188435857772134235984660852259084"));
alt_bn128_Fq2::Frobenius_coeffs_c1[0] = alt_bn128_Fq("1");
alt_bn128_Fq2::Frobenius_coeffs_c1[1] = alt_bn128_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582");
/* parameters for Fq6 */
alt_bn128_Fq6::non_residue = alt_bn128_Fq2(alt_bn128_Fq("9"),alt_bn128_Fq("1"));
alt_bn128_Fq6::Frobenius_coeffs_c1[0] = alt_bn128_Fq2(alt_bn128_Fq("1"),alt_bn128_Fq("0"));
alt_bn128_Fq6::Frobenius_coeffs_c1[1] = alt_bn128_Fq2(alt_bn128_Fq("21575463638280843010398324269430826099269044274347216827212613867836435027261"),alt_bn128_Fq("10307601595873709700152284273816112264069230130616436755625194854815875713954"));
alt_bn128_Fq6::Frobenius_coeffs_c1[2] = alt_bn128_Fq2(alt_bn128_Fq("21888242871839275220042445260109153167277707414472061641714758635765020556616"),alt_bn128_Fq("0"));
alt_bn128_Fq6::Frobenius_coeffs_c1[3] = alt_bn128_Fq2(alt_bn128_Fq("3772000881919853776433695186713858239009073593817195771773381919316419345261"),alt_bn128_Fq("2236595495967245188281701248203181795121068902605861227855261137820944008926"));
alt_bn128_Fq6::Frobenius_coeffs_c1[4] = alt_bn128_Fq2(alt_bn128_Fq("2203960485148121921418603742825762020974279258880205651966"),alt_bn128_Fq("0"));
alt_bn128_Fq6::Frobenius_coeffs_c1[5] = alt_bn128_Fq2(alt_bn128_Fq("18429021223477853657660792034369865839114504446431234726392080002137598044644"),alt_bn128_Fq("9344045779998320333812420223237981029506012124075525679208581902008406485703"));
alt_bn128_Fq6::Frobenius_coeffs_c2[0] = alt_bn128_Fq2(alt_bn128_Fq("1"),alt_bn128_Fq("0"));
alt_bn128_Fq6::Frobenius_coeffs_c2[1] = alt_bn128_Fq2(alt_bn128_Fq("2581911344467009335267311115468803099551665605076196740867805258568234346338"),alt_bn128_Fq("19937756971775647987995932169929341994314640652964949448313374472400716661030"));
alt_bn128_Fq6::Frobenius_coeffs_c2[2] = alt_bn128_Fq2(alt_bn128_Fq("2203960485148121921418603742825762020974279258880205651966"),alt_bn128_Fq("0"));
alt_bn128_Fq6::Frobenius_coeffs_c2[3] = alt_bn128_Fq2(alt_bn128_Fq("5324479202449903542726783395506214481928257762400643279780343368557297135718"),alt_bn128_Fq("16208900380737693084919495127334387981393726419856888799917914180988844123039"));
alt_bn128_Fq6::Frobenius_coeffs_c2[4] = alt_bn128_Fq2(alt_bn128_Fq("21888242871839275220042445260109153167277707414472061641714758635765020556616"),alt_bn128_Fq("0"));
alt_bn128_Fq6::Frobenius_coeffs_c2[5] = alt_bn128_Fq2(alt_bn128_Fq("13981852324922362344252311234282257507216387789820983642040889267519694726527"),alt_bn128_Fq("7629828391165209371577384193250820201684255241773809077146787135900891633097"));
/* parameters for Fq12 */
alt_bn128_Fq12::non_residue = alt_bn128_Fq2(alt_bn128_Fq("9"),alt_bn128_Fq("1"));
alt_bn128_Fq12::Frobenius_coeffs_c1[0] = alt_bn128_Fq2(alt_bn128_Fq("1"),alt_bn128_Fq("0"));
alt_bn128_Fq12::Frobenius_coeffs_c1[1] = alt_bn128_Fq2(alt_bn128_Fq("8376118865763821496583973867626364092589906065868298776909617916018768340080"),alt_bn128_Fq("16469823323077808223889137241176536799009286646108169935659301613961712198316"));
alt_bn128_Fq12::Frobenius_coeffs_c1[2] = alt_bn128_Fq2(alt_bn128_Fq("21888242871839275220042445260109153167277707414472061641714758635765020556617"),alt_bn128_Fq("0"));
alt_bn128_Fq12::Frobenius_coeffs_c1[3] = alt_bn128_Fq2(alt_bn128_Fq("11697423496358154304825782922584725312912383441159505038794027105778954184319"),alt_bn128_Fq("303847389135065887422783454877609941456349188919719272345083954437860409601"));
alt_bn128_Fq12::Frobenius_coeffs_c1[4] = alt_bn128_Fq2(alt_bn128_Fq("21888242871839275220042445260109153167277707414472061641714758635765020556616"),alt_bn128_Fq("0"));
alt_bn128_Fq12::Frobenius_coeffs_c1[5] = alt_bn128_Fq2(alt_bn128_Fq("3321304630594332808241809054958361220322477375291206261884409189760185844239"),alt_bn128_Fq("5722266937896532885780051958958348231143373700109372999374820235121374419868"));
alt_bn128_Fq12::Frobenius_coeffs_c1[6] = alt_bn128_Fq2(alt_bn128_Fq("21888242871839275222246405745257275088696311157297823662689037894645226208582"),alt_bn128_Fq("0"));
alt_bn128_Fq12::Frobenius_coeffs_c1[7] = alt_bn128_Fq2(alt_bn128_Fq("13512124006075453725662431877630910996106405091429524885779419978626457868503"),alt_bn128_Fq("5418419548761466998357268504080738289687024511189653727029736280683514010267"));
alt_bn128_Fq12::Frobenius_coeffs_c1[8] = alt_bn128_Fq2(alt_bn128_Fq("2203960485148121921418603742825762020974279258880205651966"),alt_bn128_Fq("0"));
alt_bn128_Fq12::Frobenius_coeffs_c1[9] = alt_bn128_Fq2(alt_bn128_Fq("10190819375481120917420622822672549775783927716138318623895010788866272024264"),alt_bn128_Fq("21584395482704209334823622290379665147239961968378104390343953940207365798982"));
alt_bn128_Fq12::Frobenius_coeffs_c1[10] = alt_bn128_Fq2(alt_bn128_Fq("2203960485148121921418603742825762020974279258880205651967"),alt_bn128_Fq("0"));
alt_bn128_Fq12::Frobenius_coeffs_c1[11] = alt_bn128_Fq2(alt_bn128_Fq("18566938241244942414004596690298913868373833782006617400804628704885040364344"),alt_bn128_Fq("16165975933942742336466353786298926857552937457188450663314217659523851788715"));
/* choice of short Weierstrass curve and its twist */
alt_bn128_coeff_b = alt_bn128_Fq("3");
alt_bn128_twist = alt_bn128_Fq2(alt_bn128_Fq("9"), alt_bn128_Fq("1"));
alt_bn128_twist_coeff_b = alt_bn128_coeff_b * alt_bn128_twist.inverse();
alt_bn128_twist_mul_by_b_c0 = alt_bn128_coeff_b * alt_bn128_Fq2::non_residue;
alt_bn128_twist_mul_by_b_c1 = alt_bn128_coeff_b * alt_bn128_Fq2::non_residue;
alt_bn128_twist_mul_by_q_X = alt_bn128_Fq2(alt_bn128_Fq("21575463638280843010398324269430826099269044274347216827212613867836435027261"),
alt_bn128_Fq("10307601595873709700152284273816112264069230130616436755625194854815875713954"));
alt_bn128_twist_mul_by_q_Y = alt_bn128_Fq2(alt_bn128_Fq("2821565182194536844548159561693502659359617185244120367078079554186484126554"),
alt_bn128_Fq("3505843767911556378687030309984248845540243509899259641013678093033130930403"));
/* choice of group G1 */
alt_bn128_G1::G1_zero = alt_bn128_G1(alt_bn128_Fq::zero(),
alt_bn128_Fq::one(),
alt_bn128_Fq::zero());
alt_bn128_G1::G1_one = alt_bn128_G1(alt_bn128_Fq("1"),
alt_bn128_Fq("2"),
alt_bn128_Fq::one());
alt_bn128_G1::wnaf_window_table.push_back(11);
alt_bn128_G1::wnaf_window_table.push_back(24);
alt_bn128_G1::wnaf_window_table.push_back(60);
alt_bn128_G1::wnaf_window_table.push_back(127);
alt_bn128_G1::fixed_base_exp_window_table.resize(0);
// window 1 is unbeaten in [-inf, 4.99]
alt_bn128_G1::fixed_base_exp_window_table.push_back(1);
// window 2 is unbeaten in [4.99, 10.99]
alt_bn128_G1::fixed_base_exp_window_table.push_back(5);
// window 3 is unbeaten in [10.99, 32.29]
alt_bn128_G1::fixed_base_exp_window_table.push_back(11);
// window 4 is unbeaten in [32.29, 55.23]
alt_bn128_G1::fixed_base_exp_window_table.push_back(32);
// window 5 is unbeaten in [55.23, 162.03]
alt_bn128_G1::fixed_base_exp_window_table.push_back(55);
// window 6 is unbeaten in [162.03, 360.15]
alt_bn128_G1::fixed_base_exp_window_table.push_back(162);
// window 7 is unbeaten in [360.15, 815.44]
alt_bn128_G1::fixed_base_exp_window_table.push_back(360);
// window 8 is unbeaten in [815.44, 2373.07]
alt_bn128_G1::fixed_base_exp_window_table.push_back(815);
// window 9 is unbeaten in [2373.07, 6977.75]
alt_bn128_G1::fixed_base_exp_window_table.push_back(2373);
// window 10 is unbeaten in [6977.75, 7122.23]
alt_bn128_G1::fixed_base_exp_window_table.push_back(6978);
// window 11 is unbeaten in [7122.23, 57818.46]
alt_bn128_G1::fixed_base_exp_window_table.push_back(7122);
// window 12 is never the best
alt_bn128_G1::fixed_base_exp_window_table.push_back(0);
// window 13 is unbeaten in [57818.46, 169679.14]
alt_bn128_G1::fixed_base_exp_window_table.push_back(57818);
// window 14 is never the best
alt_bn128_G1::fixed_base_exp_window_table.push_back(0);
// window 15 is unbeaten in [169679.14, 439758.91]
alt_bn128_G1::fixed_base_exp_window_table.push_back(169679);
// window 16 is unbeaten in [439758.91, 936073.41]
alt_bn128_G1::fixed_base_exp_window_table.push_back(439759);
// window 17 is unbeaten in [936073.41, 4666554.74]
alt_bn128_G1::fixed_base_exp_window_table.push_back(936073);
// window 18 is never the best
alt_bn128_G1::fixed_base_exp_window_table.push_back(0);
// window 19 is unbeaten in [4666554.74, 7580404.42]
alt_bn128_G1::fixed_base_exp_window_table.push_back(4666555);
// window 20 is unbeaten in [7580404.42, 34552892.20]
alt_bn128_G1::fixed_base_exp_window_table.push_back(7580404);
// window 21 is never the best
alt_bn128_G1::fixed_base_exp_window_table.push_back(0);
// window 22 is unbeaten in [34552892.20, inf]
alt_bn128_G1::fixed_base_exp_window_table.push_back(34552892);
/* choice of group G2 */
alt_bn128_G2::G2_zero = alt_bn128_G2(alt_bn128_Fq2::zero(),
alt_bn128_Fq2::one(),
alt_bn128_Fq2::zero());
alt_bn128_G2::G2_one = alt_bn128_G2(alt_bn128_Fq2(alt_bn128_Fq("10857046999023057135944570762232829481370756359578518086990519993285655852781"),
alt_bn128_Fq("11559732032986387107991004021392285783925812861821192530917403151452391805634")),
alt_bn128_Fq2(alt_bn128_Fq("8495653923123431417604973247489272438418190587263600148770280649306958101930"),
alt_bn128_Fq("4082367875863433681332203403145435568316851327593401208105741076214120093531")),
alt_bn128_Fq2::one());
alt_bn128_G2::wnaf_window_table.push_back(5);
alt_bn128_G2::wnaf_window_table.push_back(15);
alt_bn128_G2::wnaf_window_table.push_back(39);
alt_bn128_G2::wnaf_window_table.push_back(109);
alt_bn128_G2::fixed_base_exp_window_table.resize(0);
// window 1 is unbeaten in [-inf, 5.10]
alt_bn128_G2::fixed_base_exp_window_table.push_back(1);
// window 2 is unbeaten in [5.10, 10.43]
alt_bn128_G2::fixed_base_exp_window_table.push_back(5);
// window 3 is unbeaten in [10.43, 25.28]
alt_bn128_G2::fixed_base_exp_window_table.push_back(10);
// window 4 is unbeaten in [25.28, 59.00]
alt_bn128_G2::fixed_base_exp_window_table.push_back(25);
// window 5 is unbeaten in [59.00, 154.03]
alt_bn128_G2::fixed_base_exp_window_table.push_back(59);
// window 6 is unbeaten in [154.03, 334.25]
alt_bn128_G2::fixed_base_exp_window_table.push_back(154);
// window 7 is unbeaten in [334.25, 742.58]
alt_bn128_G2::fixed_base_exp_window_table.push_back(334);
// window 8 is unbeaten in [742.58, 2034.40]
alt_bn128_G2::fixed_base_exp_window_table.push_back(743);
// window 9 is unbeaten in [2034.40, 4987.56]
alt_bn128_G2::fixed_base_exp_window_table.push_back(2034);
// window 10 is unbeaten in [4987.56, 8888.27]
alt_bn128_G2::fixed_base_exp_window_table.push_back(4988);
// window 11 is unbeaten in [8888.27, 26271.13]
alt_bn128_G2::fixed_base_exp_window_table.push_back(8888);
// window 12 is unbeaten in [26271.13, 39768.20]
alt_bn128_G2::fixed_base_exp_window_table.push_back(26271);
// window 13 is unbeaten in [39768.20, 106275.75]
alt_bn128_G2::fixed_base_exp_window_table.push_back(39768);
// window 14 is unbeaten in [106275.75, 141703.40]
alt_bn128_G2::fixed_base_exp_window_table.push_back(106276);
// window 15 is unbeaten in [141703.40, 462422.97]
alt_bn128_G2::fixed_base_exp_window_table.push_back(141703);
// window 16 is unbeaten in [462422.97, 926871.84]
alt_bn128_G2::fixed_base_exp_window_table.push_back(462423);
// window 17 is unbeaten in [926871.84, 4873049.17]
alt_bn128_G2::fixed_base_exp_window_table.push_back(926872);
// window 18 is never the best
alt_bn128_G2::fixed_base_exp_window_table.push_back(0);
// window 19 is unbeaten in [4873049.17, 5706707.88]
alt_bn128_G2::fixed_base_exp_window_table.push_back(4873049);
// window 20 is unbeaten in [5706707.88, 31673814.95]
alt_bn128_G2::fixed_base_exp_window_table.push_back(5706708);
// window 21 is never the best
alt_bn128_G2::fixed_base_exp_window_table.push_back(0);
// window 22 is unbeaten in [31673814.95, inf]
alt_bn128_G2::fixed_base_exp_window_table.push_back(31673815);
/* pairing parameters */
alt_bn128_ate_loop_count = bigint_q("29793968203157093288");
alt_bn128_ate_is_loop_count_neg = false;
alt_bn128_final_exponent = bigint<12*alt_bn128_q_limbs>("552484233613224096312617126783173147097382103762957654188882734314196910839907541213974502761540629817009608548654680343627701153829446747810907373256841551006201639677726139946029199968412598804882391702273019083653272047566316584365559776493027495458238373902875937659943504873220554161550525926302303331747463515644711876653177129578303191095900909191624817826566688241804408081892785725967931714097716709526092261278071952560171111444072049229123565057483750161460024353346284167282452756217662335528813519139808291170539072125381230815729071544861602750936964829313608137325426383735122175229541155376346436093930287402089517426973178917569713384748081827255472576937471496195752727188261435633271238710131736096299798168852925540549342330775279877006784354801422249722573783561685179618816480037695005515426162362431072245638324744480");
alt_bn128_final_exponent_z = bigint_q("4965661367192848881");
alt_bn128_final_exponent_is_z_neg = false;
}
} // libsnark

View File

@@ -0,0 +1,57 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef ALT_BN128_INIT_HPP_
#define ALT_BN128_INIT_HPP_
#include "algebra/curves/public_params.hpp"
#include "algebra/fields/fp.hpp"
#include "algebra/fields/fp2.hpp"
#include "algebra/fields/fp6_3over2.hpp"
#include "algebra/fields/fp12_2over3over2.hpp"
namespace libsnark {
const mp_size_t alt_bn128_r_bitcount = 254;
const mp_size_t alt_bn128_q_bitcount = 254;
const mp_size_t alt_bn128_r_limbs = (alt_bn128_r_bitcount+GMP_NUMB_BITS-1)/GMP_NUMB_BITS;
const mp_size_t alt_bn128_q_limbs = (alt_bn128_q_bitcount+GMP_NUMB_BITS-1)/GMP_NUMB_BITS;
extern bigint<alt_bn128_r_limbs> alt_bn128_modulus_r;
extern bigint<alt_bn128_q_limbs> alt_bn128_modulus_q;
typedef Fp_model<alt_bn128_r_limbs, alt_bn128_modulus_r> alt_bn128_Fr;
typedef Fp_model<alt_bn128_q_limbs, alt_bn128_modulus_q> alt_bn128_Fq;
typedef Fp2_model<alt_bn128_q_limbs, alt_bn128_modulus_q> alt_bn128_Fq2;
typedef Fp6_3over2_model<alt_bn128_q_limbs, alt_bn128_modulus_q> alt_bn128_Fq6;
typedef Fp12_2over3over2_model<alt_bn128_q_limbs, alt_bn128_modulus_q> alt_bn128_Fq12;
typedef alt_bn128_Fq12 alt_bn128_GT;
// parameters for Barreto--Naehrig curve E/Fq : y^2 = x^3 + b
extern alt_bn128_Fq alt_bn128_coeff_b;
// parameters for twisted Barreto--Naehrig curve E'/Fq2 : y^2 = x^3 + b/xi
extern alt_bn128_Fq2 alt_bn128_twist;
extern alt_bn128_Fq2 alt_bn128_twist_coeff_b;
extern alt_bn128_Fq alt_bn128_twist_mul_by_b_c0;
extern alt_bn128_Fq alt_bn128_twist_mul_by_b_c1;
extern alt_bn128_Fq2 alt_bn128_twist_mul_by_q_X;
extern alt_bn128_Fq2 alt_bn128_twist_mul_by_q_Y;
// parameters for pairing
extern bigint<alt_bn128_q_limbs> alt_bn128_ate_loop_count;
extern bool alt_bn128_ate_is_loop_count_neg;
extern bigint<12*alt_bn128_q_limbs> alt_bn128_final_exponent;
extern bigint<alt_bn128_q_limbs> alt_bn128_final_exponent_z;
extern bool alt_bn128_final_exponent_is_z_neg;
void init_alt_bn128_params();
class alt_bn128_G1;
class alt_bn128_G2;
} // libsnark
#endif // ALT_BN128_INIT_HPP_

View File

@@ -0,0 +1,547 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "algebra/curves/alt_bn128/alt_bn128_pairing.hpp"
#include "algebra/curves/alt_bn128/alt_bn128_init.hpp"
#include "algebra/curves/alt_bn128/alt_bn128_g1.hpp"
#include "algebra/curves/alt_bn128/alt_bn128_g2.hpp"
#include <cassert>
#include "common/profiling.hpp"
#include "common/assert_except.hpp"
namespace libsnark {
bool alt_bn128_ate_G1_precomp::operator==(const alt_bn128_ate_G1_precomp &other) const
{
return (this->PX == other.PX &&
this->PY == other.PY);
}
std::ostream& operator<<(std::ostream &out, const alt_bn128_ate_G1_precomp &prec_P)
{
out << prec_P.PX << OUTPUT_SEPARATOR << prec_P.PY;
return out;
}
std::istream& operator>>(std::istream &in, alt_bn128_ate_G1_precomp &prec_P)
{
in >> prec_P.PX;
consume_OUTPUT_SEPARATOR(in);
in >> prec_P.PY;
return in;
}
bool alt_bn128_ate_ell_coeffs::operator==(const alt_bn128_ate_ell_coeffs &other) const
{
return (this->ell_0 == other.ell_0 &&
this->ell_VW == other.ell_VW &&
this->ell_VV == other.ell_VV);
}
std::ostream& operator<<(std::ostream &out, const alt_bn128_ate_ell_coeffs &c)
{
out << c.ell_0 << OUTPUT_SEPARATOR << c.ell_VW << OUTPUT_SEPARATOR << c.ell_VV;
return out;
}
std::istream& operator>>(std::istream &in, alt_bn128_ate_ell_coeffs &c)
{
in >> c.ell_0;
consume_OUTPUT_SEPARATOR(in);
in >> c.ell_VW;
consume_OUTPUT_SEPARATOR(in);
in >> c.ell_VV;
return in;
}
bool alt_bn128_ate_G2_precomp::operator==(const alt_bn128_ate_G2_precomp &other) const
{
return (this->QX == other.QX &&
this->QY == other.QY &&
this->coeffs == other.coeffs);
}
std::ostream& operator<<(std::ostream& out, const alt_bn128_ate_G2_precomp &prec_Q)
{
out << prec_Q.QX << OUTPUT_SEPARATOR << prec_Q.QY << "\n";
out << prec_Q.coeffs.size() << "\n";
for (const alt_bn128_ate_ell_coeffs &c : prec_Q.coeffs)
{
out << c << OUTPUT_NEWLINE;
}
return out;
}
std::istream& operator>>(std::istream& in, alt_bn128_ate_G2_precomp &prec_Q)
{
in >> prec_Q.QX;
consume_OUTPUT_SEPARATOR(in);
in >> prec_Q.QY;
consume_newline(in);
prec_Q.coeffs.clear();
size_t s;
in >> s;
consume_newline(in);
prec_Q.coeffs.reserve(s);
for (size_t i = 0; i < s; ++i)
{
alt_bn128_ate_ell_coeffs c;
in >> c;
consume_OUTPUT_NEWLINE(in);
prec_Q.coeffs.emplace_back(c);
}
return in;
}
/* final exponentiations */
alt_bn128_Fq12 alt_bn128_final_exponentiation_first_chunk(const alt_bn128_Fq12 &elt)
{
enter_block("Call to alt_bn128_final_exponentiation_first_chunk");
/*
Computes result = elt^((q^6-1)*(q^2+1)).
Follows, e.g., Beuchat et al page 9, by computing result as follows:
elt^((q^6-1)*(q^2+1)) = (conj(elt) * elt^(-1))^(q^2+1)
More precisely:
A = conj(elt)
B = elt.inverse()
C = A * B
D = C.Frobenius_map(2)
result = D * C
*/
const alt_bn128_Fq12 A = alt_bn128_Fq12(elt.c0,-elt.c1);
const alt_bn128_Fq12 B = elt.inverse();
const alt_bn128_Fq12 C = A * B;
const alt_bn128_Fq12 D = C.Frobenius_map(2);
const alt_bn128_Fq12 result = D * C;
leave_block("Call to alt_bn128_final_exponentiation_first_chunk");
return result;
}
alt_bn128_Fq12 alt_bn128_exp_by_neg_z(const alt_bn128_Fq12 &elt)
{
enter_block("Call to alt_bn128_exp_by_neg_z");
alt_bn128_Fq12 result = elt.cyclotomic_exp(alt_bn128_final_exponent_z);
if (!alt_bn128_final_exponent_is_z_neg)
{
result = result.unitary_inverse();
}
leave_block("Call to alt_bn128_exp_by_neg_z");
return result;
}
alt_bn128_Fq12 alt_bn128_final_exponentiation_last_chunk(const alt_bn128_Fq12 &elt)
{
enter_block("Call to alt_bn128_final_exponentiation_last_chunk");
/*
Follows Laura Fuentes-Castaneda et al. "Faster hashing to G2"
by computing:
result = elt^(q^3 * (12*z^3 + 6z^2 + 4z - 1) +
q^2 * (12*z^3 + 6z^2 + 6z) +
q * (12*z^3 + 6z^2 + 4z) +
1 * (12*z^3 + 12z^2 + 6z + 1))
which equals
result = elt^( 2z * ( 6z^2 + 3z + 1 ) * (q^4 - q^2 + 1)/r ).
Using the following addition chain:
A = exp_by_neg_z(elt) // = elt^(-z)
B = A^2 // = elt^(-2*z)
C = B^2 // = elt^(-4*z)
D = C * B // = elt^(-6*z)
E = exp_by_neg_z(D) // = elt^(6*z^2)
F = E^2 // = elt^(12*z^2)
G = epx_by_neg_z(F) // = elt^(-12*z^3)
H = conj(D) // = elt^(6*z)
I = conj(G) // = elt^(12*z^3)
J = I * E // = elt^(12*z^3 + 6*z^2)
K = J * H // = elt^(12*z^3 + 6*z^2 + 6*z)
L = K * B // = elt^(12*z^3 + 6*z^2 + 4*z)
M = K * E // = elt^(12*z^3 + 12*z^2 + 6*z)
N = M * elt // = elt^(12*z^3 + 12*z^2 + 6*z + 1)
O = L.Frobenius_map(1) // = elt^(q*(12*z^3 + 6*z^2 + 4*z))
P = O * N // = elt^(q*(12*z^3 + 6*z^2 + 4*z) * (12*z^3 + 12*z^2 + 6*z + 1))
Q = K.Frobenius_map(2) // = elt^(q^2 * (12*z^3 + 6*z^2 + 6*z))
R = Q * P // = elt^(q^2 * (12*z^3 + 6*z^2 + 6*z) + q*(12*z^3 + 6*z^2 + 4*z) * (12*z^3 + 12*z^2 + 6*z + 1))
S = conj(elt) // = elt^(-1)
T = S * L // = elt^(12*z^3 + 6*z^2 + 4*z - 1)
U = T.Frobenius_map(3) // = elt^(q^3(12*z^3 + 6*z^2 + 4*z - 1))
V = U * R // = elt^(q^3(12*z^3 + 6*z^2 + 4*z - 1) + q^2 * (12*z^3 + 6*z^2 + 6*z) + q*(12*z^3 + 6*z^2 + 4*z) * (12*z^3 + 12*z^2 + 6*z + 1))
result = V
*/
const alt_bn128_Fq12 A = alt_bn128_exp_by_neg_z(elt);
const alt_bn128_Fq12 B = A.cyclotomic_squared();
const alt_bn128_Fq12 C = B.cyclotomic_squared();
const alt_bn128_Fq12 D = C * B;
const alt_bn128_Fq12 E = alt_bn128_exp_by_neg_z(D);
const alt_bn128_Fq12 F = E.cyclotomic_squared();
const alt_bn128_Fq12 G = alt_bn128_exp_by_neg_z(F);
const alt_bn128_Fq12 H = D.unitary_inverse();
const alt_bn128_Fq12 I = G.unitary_inverse();
const alt_bn128_Fq12 J = I * E;
const alt_bn128_Fq12 K = J * H;
const alt_bn128_Fq12 L = K * B;
const alt_bn128_Fq12 M = K * E;
const alt_bn128_Fq12 N = M * elt;
const alt_bn128_Fq12 O = L.Frobenius_map(1);
const alt_bn128_Fq12 P = O * N;
const alt_bn128_Fq12 Q = K.Frobenius_map(2);
const alt_bn128_Fq12 R = Q * P;
const alt_bn128_Fq12 S = elt.unitary_inverse();
const alt_bn128_Fq12 T = S * L;
const alt_bn128_Fq12 U = T.Frobenius_map(3);
const alt_bn128_Fq12 V = U * R;
const alt_bn128_Fq12 result = V;
leave_block("Call to alt_bn128_final_exponentiation_last_chunk");
return result;
}
alt_bn128_GT alt_bn128_final_exponentiation(const alt_bn128_Fq12 &elt)
{
enter_block("Call to alt_bn128_final_exponentiation");
/* OLD naive version:
alt_bn128_GT result = elt^alt_bn128_final_exponent;
*/
alt_bn128_Fq12 A = alt_bn128_final_exponentiation_first_chunk(elt);
alt_bn128_GT result = alt_bn128_final_exponentiation_last_chunk(A);
leave_block("Call to alt_bn128_final_exponentiation");
return result;
}
/* ate pairing */
void doubling_step_for_flipped_miller_loop(const alt_bn128_Fq two_inv,
alt_bn128_G2 &current,
alt_bn128_ate_ell_coeffs &c)
{
const alt_bn128_Fq2 X = current.X, Y = current.Y, Z = current.Z;
const alt_bn128_Fq2 A = two_inv * (X * Y); // A = X1 * Y1 / 2
const alt_bn128_Fq2 B = Y.squared(); // B = Y1^2
const alt_bn128_Fq2 C = Z.squared(); // C = Z1^2
const alt_bn128_Fq2 D = C+C+C; // D = 3 * C
const alt_bn128_Fq2 E = alt_bn128_twist_coeff_b * D; // E = twist_b * D
const alt_bn128_Fq2 F = E+E+E; // F = 3 * E
const alt_bn128_Fq2 G = two_inv * (B+F); // G = (B+F)/2
const alt_bn128_Fq2 H = (Y+Z).squared() - (B+C); // H = (Y1+Z1)^2-(B+C)
const alt_bn128_Fq2 I = E-B; // I = E-B
const alt_bn128_Fq2 J = X.squared(); // J = X1^2
const alt_bn128_Fq2 E_squared = E.squared(); // E_squared = E^2
current.X = A * (B-F); // X3 = A * (B-F)
current.Y = G.squared() - (E_squared+E_squared+E_squared); // Y3 = G^2 - 3*E^2
current.Z = B * H; // Z3 = B * H
c.ell_0 = alt_bn128_twist * I; // ell_0 = xi * I
c.ell_VW = -H; // ell_VW = - H (later: * yP)
c.ell_VV = J+J+J; // ell_VV = 3*J (later: * xP)
}
void mixed_addition_step_for_flipped_miller_loop(const alt_bn128_G2 base,
alt_bn128_G2 &current,
alt_bn128_ate_ell_coeffs &c)
{
const alt_bn128_Fq2 X1 = current.X, Y1 = current.Y, Z1 = current.Z;
const alt_bn128_Fq2 &x2 = base.X, &y2 = base.Y;
const alt_bn128_Fq2 D = X1 - x2 * Z1; // D = X1 - X2*Z1
const alt_bn128_Fq2 E = Y1 - y2 * Z1; // E = Y1 - Y2*Z1
const alt_bn128_Fq2 F = D.squared(); // F = D^2
const alt_bn128_Fq2 G = E.squared(); // G = E^2
const alt_bn128_Fq2 H = D*F; // H = D*F
const alt_bn128_Fq2 I = X1 * F; // I = X1 * F
const alt_bn128_Fq2 J = H + Z1*G - (I+I); // J = H + Z1*G - (I+I)
current.X = D * J; // X3 = D*J
current.Y = E * (I-J)-(H * Y1); // Y3 = E*(I-J)-(H*Y1)
current.Z = Z1 * H; // Z3 = Z1*H
c.ell_0 = alt_bn128_twist * (E * x2 - D * y2); // ell_0 = xi * (E * X2 - D * Y2)
c.ell_VV = - E; // ell_VV = - E (later: * xP)
c.ell_VW = D; // ell_VW = D (later: * yP )
}
alt_bn128_ate_G1_precomp alt_bn128_ate_precompute_G1(const alt_bn128_G1& P)
{
enter_block("Call to alt_bn128_ate_precompute_G1");
alt_bn128_G1 Pcopy = P;
Pcopy.to_affine_coordinates();
alt_bn128_ate_G1_precomp result;
result.PX = Pcopy.X;
result.PY = Pcopy.Y;
leave_block("Call to alt_bn128_ate_precompute_G1");
return result;
}
alt_bn128_ate_G2_precomp alt_bn128_ate_precompute_G2(const alt_bn128_G2& Q)
{
enter_block("Call to alt_bn128_ate_precompute_G2");
alt_bn128_G2 Qcopy(Q);
Qcopy.to_affine_coordinates();
alt_bn128_Fq two_inv = (alt_bn128_Fq("2").inverse()); // could add to global params if needed
alt_bn128_ate_G2_precomp result;
result.QX = Qcopy.X;
result.QY = Qcopy.Y;
alt_bn128_G2 R;
R.X = Qcopy.X;
R.Y = Qcopy.Y;
R.Z = alt_bn128_Fq2::one();
const bigint<alt_bn128_Fr::num_limbs> &loop_count = alt_bn128_ate_loop_count;
bool found_one = false;
alt_bn128_ate_ell_coeffs c;
for (long i = loop_count.max_bits(); i >= 0; --i)
{
const bool bit = loop_count.test_bit(i);
if (!found_one)
{
/* this skips the MSB itself */
found_one |= bit;
continue;
}
doubling_step_for_flipped_miller_loop(two_inv, R, c);
result.coeffs.push_back(c);
if (bit)
{
mixed_addition_step_for_flipped_miller_loop(Qcopy, R, c);
result.coeffs.push_back(c);
}
}
alt_bn128_G2 Q1 = Qcopy.mul_by_q();
assert_except(Q1.Z == alt_bn128_Fq2::one());
alt_bn128_G2 Q2 = Q1.mul_by_q();
assert_except(Q2.Z == alt_bn128_Fq2::one());
if (alt_bn128_ate_is_loop_count_neg)
{
R.Y = - R.Y;
}
Q2.Y = - Q2.Y;
mixed_addition_step_for_flipped_miller_loop(Q1, R, c);
result.coeffs.push_back(c);
mixed_addition_step_for_flipped_miller_loop(Q2, R, c);
result.coeffs.push_back(c);
leave_block("Call to alt_bn128_ate_precompute_G2");
return result;
}
alt_bn128_Fq12 alt_bn128_ate_miller_loop(const alt_bn128_ate_G1_precomp &prec_P,
const alt_bn128_ate_G2_precomp &prec_Q)
{
enter_block("Call to alt_bn128_ate_miller_loop");
alt_bn128_Fq12 f = alt_bn128_Fq12::one();
bool found_one = false;
size_t idx = 0;
const bigint<alt_bn128_Fr::num_limbs> &loop_count = alt_bn128_ate_loop_count;
alt_bn128_ate_ell_coeffs c;
for (long i = loop_count.max_bits(); i >= 0; --i)
{
const bool bit = loop_count.test_bit(i);
if (!found_one)
{
/* this skips the MSB itself */
found_one |= bit;
continue;
}
/* code below gets executed for all bits (EXCEPT the MSB itself) of
alt_bn128_param_p (skipping leading zeros) in MSB to LSB
order */
c = prec_Q.coeffs[idx++];
f = f.squared();
f = f.mul_by_024(c.ell_0, prec_P.PY * c.ell_VW, prec_P.PX * c.ell_VV);
if (bit)
{
c = prec_Q.coeffs[idx++];
f = f.mul_by_024(c.ell_0, prec_P.PY * c.ell_VW, prec_P.PX * c.ell_VV);
}
}
if (alt_bn128_ate_is_loop_count_neg)
{
f = f.inverse();
}
c = prec_Q.coeffs[idx++];
f = f.mul_by_024(c.ell_0,prec_P.PY * c.ell_VW,prec_P.PX * c.ell_VV);
c = prec_Q.coeffs[idx++];
f = f.mul_by_024(c.ell_0,prec_P.PY * c.ell_VW,prec_P.PX * c.ell_VV);
leave_block("Call to alt_bn128_ate_miller_loop");
return f;
}
alt_bn128_Fq12 alt_bn128_ate_double_miller_loop(const alt_bn128_ate_G1_precomp &prec_P1,
const alt_bn128_ate_G2_precomp &prec_Q1,
const alt_bn128_ate_G1_precomp &prec_P2,
const alt_bn128_ate_G2_precomp &prec_Q2)
{
enter_block("Call to alt_bn128_ate_double_miller_loop");
alt_bn128_Fq12 f = alt_bn128_Fq12::one();
bool found_one = false;
size_t idx = 0;
const bigint<alt_bn128_Fr::num_limbs> &loop_count = alt_bn128_ate_loop_count;
for (long i = loop_count.max_bits(); i >= 0; --i)
{
const bool bit = loop_count.test_bit(i);
if (!found_one)
{
/* this skips the MSB itself */
found_one |= bit;
continue;
}
/* code below gets executed for all bits (EXCEPT the MSB itself) of
alt_bn128_param_p (skipping leading zeros) in MSB to LSB
order */
alt_bn128_ate_ell_coeffs c1 = prec_Q1.coeffs[idx];
alt_bn128_ate_ell_coeffs c2 = prec_Q2.coeffs[idx];
++idx;
f = f.squared();
f = f.mul_by_024(c1.ell_0, prec_P1.PY * c1.ell_VW, prec_P1.PX * c1.ell_VV);
f = f.mul_by_024(c2.ell_0, prec_P2.PY * c2.ell_VW, prec_P2.PX * c2.ell_VV);
if (bit)
{
alt_bn128_ate_ell_coeffs c1 = prec_Q1.coeffs[idx];
alt_bn128_ate_ell_coeffs c2 = prec_Q2.coeffs[idx];
++idx;
f = f.mul_by_024(c1.ell_0, prec_P1.PY * c1.ell_VW, prec_P1.PX * c1.ell_VV);
f = f.mul_by_024(c2.ell_0, prec_P2.PY * c2.ell_VW, prec_P2.PX * c2.ell_VV);
}
}
if (alt_bn128_ate_is_loop_count_neg)
{
f = f.inverse();
}
alt_bn128_ate_ell_coeffs c1 = prec_Q1.coeffs[idx];
alt_bn128_ate_ell_coeffs c2 = prec_Q2.coeffs[idx];
++idx;
f = f.mul_by_024(c1.ell_0, prec_P1.PY * c1.ell_VW, prec_P1.PX * c1.ell_VV);
f = f.mul_by_024(c2.ell_0, prec_P2.PY * c2.ell_VW, prec_P2.PX * c2.ell_VV);
c1 = prec_Q1.coeffs[idx];
c2 = prec_Q2.coeffs[idx];
++idx;
f = f.mul_by_024(c1.ell_0, prec_P1.PY * c1.ell_VW, prec_P1.PX * c1.ell_VV);
f = f.mul_by_024(c2.ell_0, prec_P2.PY * c2.ell_VW, prec_P2.PX * c2.ell_VV);
leave_block("Call to alt_bn128_ate_double_miller_loop");
return f;
}
alt_bn128_Fq12 alt_bn128_ate_pairing(const alt_bn128_G1& P, const alt_bn128_G2 &Q)
{
enter_block("Call to alt_bn128_ate_pairing");
alt_bn128_ate_G1_precomp prec_P = alt_bn128_ate_precompute_G1(P);
alt_bn128_ate_G2_precomp prec_Q = alt_bn128_ate_precompute_G2(Q);
alt_bn128_Fq12 result = alt_bn128_ate_miller_loop(prec_P, prec_Q);
leave_block("Call to alt_bn128_ate_pairing");
return result;
}
alt_bn128_GT alt_bn128_ate_reduced_pairing(const alt_bn128_G1 &P, const alt_bn128_G2 &Q)
{
enter_block("Call to alt_bn128_ate_reduced_pairing");
const alt_bn128_Fq12 f = alt_bn128_ate_pairing(P, Q);
const alt_bn128_GT result = alt_bn128_final_exponentiation(f);
leave_block("Call to alt_bn128_ate_reduced_pairing");
return result;
}
/* choice of pairing */
alt_bn128_G1_precomp alt_bn128_precompute_G1(const alt_bn128_G1& P)
{
return alt_bn128_ate_precompute_G1(P);
}
alt_bn128_G2_precomp alt_bn128_precompute_G2(const alt_bn128_G2& Q)
{
return alt_bn128_ate_precompute_G2(Q);
}
alt_bn128_Fq12 alt_bn128_miller_loop(const alt_bn128_G1_precomp &prec_P,
const alt_bn128_G2_precomp &prec_Q)
{
return alt_bn128_ate_miller_loop(prec_P, prec_Q);
}
alt_bn128_Fq12 alt_bn128_double_miller_loop(const alt_bn128_G1_precomp &prec_P1,
const alt_bn128_G2_precomp &prec_Q1,
const alt_bn128_G1_precomp &prec_P2,
const alt_bn128_G2_precomp &prec_Q2)
{
return alt_bn128_ate_double_miller_loop(prec_P1, prec_Q1, prec_P2, prec_Q2);
}
alt_bn128_Fq12 alt_bn128_pairing(const alt_bn128_G1& P,
const alt_bn128_G2 &Q)
{
return alt_bn128_ate_pairing(P, Q);
}
alt_bn128_GT alt_bn128_reduced_pairing(const alt_bn128_G1 &P,
const alt_bn128_G2 &Q)
{
return alt_bn128_ate_reduced_pairing(P, Q);
}
} // libsnark

View File

@@ -0,0 +1,92 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef ALT_BN128_PAIRING_HPP_
#define ALT_BN128_PAIRING_HPP_
#include <vector>
#include "algebra/curves/alt_bn128/alt_bn128_init.hpp"
namespace libsnark {
/* final exponentiation */
alt_bn128_GT alt_bn128_final_exponentiation(const alt_bn128_Fq12 &elt);
/* ate pairing */
struct alt_bn128_ate_G1_precomp {
alt_bn128_Fq PX;
alt_bn128_Fq PY;
bool operator==(const alt_bn128_ate_G1_precomp &other) const;
friend std::ostream& operator<<(std::ostream &out, const alt_bn128_ate_G1_precomp &prec_P);
friend std::istream& operator>>(std::istream &in, alt_bn128_ate_G1_precomp &prec_P);
};
struct alt_bn128_ate_ell_coeffs {
alt_bn128_Fq2 ell_0;
alt_bn128_Fq2 ell_VW;
alt_bn128_Fq2 ell_VV;
bool operator==(const alt_bn128_ate_ell_coeffs &other) const;
friend std::ostream& operator<<(std::ostream &out, const alt_bn128_ate_ell_coeffs &dc);
friend std::istream& operator>>(std::istream &in, alt_bn128_ate_ell_coeffs &dc);
};
struct alt_bn128_ate_G2_precomp {
alt_bn128_Fq2 QX;
alt_bn128_Fq2 QY;
std::vector<alt_bn128_ate_ell_coeffs> coeffs;
bool operator==(const alt_bn128_ate_G2_precomp &other) const;
friend std::ostream& operator<<(std::ostream &out, const alt_bn128_ate_G2_precomp &prec_Q);
friend std::istream& operator>>(std::istream &in, alt_bn128_ate_G2_precomp &prec_Q);
};
alt_bn128_ate_G1_precomp alt_bn128_ate_precompute_G1(const alt_bn128_G1& P);
alt_bn128_ate_G2_precomp alt_bn128_ate_precompute_G2(const alt_bn128_G2& Q);
alt_bn128_Fq12 alt_bn128_ate_miller_loop(const alt_bn128_ate_G1_precomp &prec_P,
const alt_bn128_ate_G2_precomp &prec_Q);
alt_bn128_Fq12 alt_bn128_ate_double_miller_loop(const alt_bn128_ate_G1_precomp &prec_P1,
const alt_bn128_ate_G2_precomp &prec_Q1,
const alt_bn128_ate_G1_precomp &prec_P2,
const alt_bn128_ate_G2_precomp &prec_Q2);
alt_bn128_Fq12 alt_bn128_ate_pairing(const alt_bn128_G1& P,
const alt_bn128_G2 &Q);
alt_bn128_GT alt_bn128_ate_reduced_pairing(const alt_bn128_G1 &P,
const alt_bn128_G2 &Q);
/* choice of pairing */
typedef alt_bn128_ate_G1_precomp alt_bn128_G1_precomp;
typedef alt_bn128_ate_G2_precomp alt_bn128_G2_precomp;
alt_bn128_G1_precomp alt_bn128_precompute_G1(const alt_bn128_G1& P);
alt_bn128_G2_precomp alt_bn128_precompute_G2(const alt_bn128_G2& Q);
alt_bn128_Fq12 alt_bn128_miller_loop(const alt_bn128_G1_precomp &prec_P,
const alt_bn128_G2_precomp &prec_Q);
alt_bn128_Fq12 alt_bn128_double_miller_loop(const alt_bn128_G1_precomp &prec_P1,
const alt_bn128_G2_precomp &prec_Q1,
const alt_bn128_G1_precomp &prec_P2,
const alt_bn128_G2_precomp &prec_Q2);
alt_bn128_Fq12 alt_bn128_pairing(const alt_bn128_G1& P,
const alt_bn128_G2 &Q);
alt_bn128_GT alt_bn128_reduced_pairing(const alt_bn128_G1 &P,
const alt_bn128_G2 &Q);
alt_bn128_GT alt_bn128_affine_reduced_pairing(const alt_bn128_G1 &P,
const alt_bn128_G2 &Q);
} // libsnark
#endif // ALT_BN128_PAIRING_HPP_

View File

@@ -0,0 +1,58 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp"
namespace libsnark {
void alt_bn128_pp::init_public_params()
{
init_alt_bn128_params();
}
alt_bn128_GT alt_bn128_pp::final_exponentiation(const alt_bn128_Fq12 &elt)
{
return alt_bn128_final_exponentiation(elt);
}
alt_bn128_G1_precomp alt_bn128_pp::precompute_G1(const alt_bn128_G1 &P)
{
return alt_bn128_precompute_G1(P);
}
alt_bn128_G2_precomp alt_bn128_pp::precompute_G2(const alt_bn128_G2 &Q)
{
return alt_bn128_precompute_G2(Q);
}
alt_bn128_Fq12 alt_bn128_pp::miller_loop(const alt_bn128_G1_precomp &prec_P,
const alt_bn128_G2_precomp &prec_Q)
{
return alt_bn128_miller_loop(prec_P, prec_Q);
}
alt_bn128_Fq12 alt_bn128_pp::double_miller_loop(const alt_bn128_G1_precomp &prec_P1,
const alt_bn128_G2_precomp &prec_Q1,
const alt_bn128_G1_precomp &prec_P2,
const alt_bn128_G2_precomp &prec_Q2)
{
return alt_bn128_double_miller_loop(prec_P1, prec_Q1, prec_P2, prec_Q2);
}
alt_bn128_Fq12 alt_bn128_pp::pairing(const alt_bn128_G1 &P,
const alt_bn128_G2 &Q)
{
return alt_bn128_pairing(P, Q);
}
alt_bn128_Fq12 alt_bn128_pp::reduced_pairing(const alt_bn128_G1 &P,
const alt_bn128_G2 &Q)
{
return alt_bn128_reduced_pairing(P, Q);
}
} // libsnark

View File

@@ -0,0 +1,50 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef ALT_BN128_PP_HPP_
#define ALT_BN128_PP_HPP_
#include "algebra/curves/public_params.hpp"
#include "algebra/curves/alt_bn128/alt_bn128_init.hpp"
#include "algebra/curves/alt_bn128/alt_bn128_g1.hpp"
#include "algebra/curves/alt_bn128/alt_bn128_g2.hpp"
#include "algebra/curves/alt_bn128/alt_bn128_pairing.hpp"
namespace libsnark {
class alt_bn128_pp {
public:
typedef alt_bn128_Fr Fp_type;
typedef alt_bn128_G1 G1_type;
typedef alt_bn128_G2 G2_type;
typedef alt_bn128_G1_precomp G1_precomp_type;
typedef alt_bn128_G2_precomp G2_precomp_type;
typedef alt_bn128_Fq Fq_type;
typedef alt_bn128_Fq2 Fqe_type;
typedef alt_bn128_Fq12 Fqk_type;
typedef alt_bn128_GT GT_type;
static const bool has_affine_pairing = false;
static void init_public_params();
static alt_bn128_GT final_exponentiation(const alt_bn128_Fq12 &elt);
static alt_bn128_G1_precomp precompute_G1(const alt_bn128_G1 &P);
static alt_bn128_G2_precomp precompute_G2(const alt_bn128_G2 &Q);
static alt_bn128_Fq12 miller_loop(const alt_bn128_G1_precomp &prec_P,
const alt_bn128_G2_precomp &prec_Q);
static alt_bn128_Fq12 double_miller_loop(const alt_bn128_G1_precomp &prec_P1,
const alt_bn128_G2_precomp &prec_Q1,
const alt_bn128_G1_precomp &prec_P2,
const alt_bn128_G2_precomp &prec_Q2);
static alt_bn128_Fq12 pairing(const alt_bn128_G1 &P,
const alt_bn128_G2 &Q);
static alt_bn128_Fq12 reduced_pairing(const alt_bn128_G1 &P,
const alt_bn128_G2 &Q);
};
} // libsnark
#endif // ALT_BN128_PP_HPP_

View File

@@ -0,0 +1,22 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef CURVE_UTILS_HPP_
#define CURVE_UTILS_HPP_
#include <cstdint>
#include "algebra/fields/bigint.hpp"
namespace libsnark {
template<typename GroupT, mp_size_t m>
GroupT scalar_mul(const GroupT &base, const bigint<m> &scalar);
} // libsnark
#include "algebra/curves/curve_utils.tcc"
#endif // CURVE_UTILS_HPP_

View File

@@ -0,0 +1,37 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef CURVE_UTILS_TCC_
#define CURVE_UTILS_TCC_
namespace libsnark {
template<typename GroupT, mp_size_t m>
GroupT scalar_mul(const GroupT &base, const bigint<m> &scalar)
{
GroupT result = GroupT::zero();
bool found_one = false;
for (long i = scalar.max_bits() - 1; i >= 0; --i)
{
if (found_one)
{
result = result.dbl();
}
if (scalar.test_bit(i))
{
found_one = true;
result = result + base;
}
}
return result;
}
} // libsnark
#endif // CURVE_UTILS_TCC_

View File

@@ -0,0 +1,103 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef PUBLIC_PARAMS_HPP_
#define PUBLIC_PARAMS_HPP_
#include <vector>
namespace libsnark {
/*
for every curve the user should define corresponding
public_params with the following typedefs:
Fp_type
G1_type
G2_type
G1_precomp_type
G2_precomp_type
affine_ate_G1_precomp_type
affine_ate_G2_precomp_type
Fq_type
Fqe_type
Fqk_type
GT_type
one should also define the following static methods:
void init_public_params();
GT<EC_ppT> final_exponentiation(const Fqk<EC_ppT> &elt);
G1_precomp<EC_ppT> precompute_G1(const G1<EC_ppT> &P);
G2_precomp<EC_ppT> precompute_G2(const G2<EC_ppT> &Q);
Fqk<EC_ppT> miller_loop(const G1_precomp<EC_ppT> &prec_P,
const G2_precomp<EC_ppT> &prec_Q);
affine_ate_G1_precomp<EC_ppT> affine_ate_precompute_G1(const G1<EC_ppT> &P);
affine_ate_G2_precomp<EC_ppT> affine_ate_precompute_G2(const G2<EC_ppT> &Q);
Fqk<EC_ppT> affine_ate_miller_loop(const affine_ate_G1_precomp<EC_ppT> &prec_P,
const affine_ate_G2_precomp<EC_ppT> &prec_Q);
Fqk<EC_ppT> affine_ate_e_over_e_miller_loop(const affine_ate_G1_precomp<EC_ppT> &prec_P1,
const affine_ate_G2_precomp<EC_ppT> &prec_Q1,
const affine_ate_G1_precomp<EC_ppT> &prec_P2,
const affine_ate_G2_precomp<EC_ppT> &prec_Q2);
Fqk<EC_ppT> affine_ate_e_times_e_over_e_miller_loop(const affine_ate_G1_precomp<EC_ppT> &prec_P1,
const affine_ate_G2_precomp<EC_ppT> &prec_Q1,
const affine_ate_G1_precomp<EC_ppT> &prec_P2,
const affine_ate_G2_precomp<EC_ppT> &prec_Q2,
const affine_ate_G1_precomp<EC_ppT> &prec_P3,
const affine_ate_G2_precomp<EC_ppT> &prec_Q3);
Fqk<EC_ppT> double_miller_loop(const G1_precomp<EC_ppT> &prec_P1,
const G2_precomp<EC_ppT> &prec_Q1,
const G1_precomp<EC_ppT> &prec_P2,
const G2_precomp<EC_ppT> &prec_Q2);
Fqk<EC_ppT> pairing(const G1<EC_ppT> &P,
const G2<EC_ppT> &Q);
GT<EC_ppT> reduced_pairing(const G1<EC_ppT> &P,
const G2<EC_ppT> &Q);
GT<EC_ppT> affine_reduced_pairing(const G1<EC_ppT> &P,
const G2<EC_ppT> &Q);
*/
template<typename EC_ppT>
using Fr = typename EC_ppT::Fp_type;
template<typename EC_ppT>
using G1 = typename EC_ppT::G1_type;
template<typename EC_ppT>
using G2 = typename EC_ppT::G2_type;
template<typename EC_ppT>
using G1_precomp = typename EC_ppT::G1_precomp_type;
template<typename EC_ppT>
using G2_precomp = typename EC_ppT::G2_precomp_type;
template<typename EC_ppT>
using affine_ate_G1_precomp = typename EC_ppT::affine_ate_G1_precomp_type;
template<typename EC_ppT>
using affine_ate_G2_precomp = typename EC_ppT::affine_ate_G2_precomp_type;
template<typename EC_ppT>
using Fq = typename EC_ppT::Fq_type;
template<typename EC_ppT>
using Fqe = typename EC_ppT::Fqe_type;
template<typename EC_ppT>
using Fqk = typename EC_ppT::Fqk_type;
template<typename EC_ppT>
using GT = typename EC_ppT::GT_type;
template<typename EC_ppT>
using Fr_vector = std::vector<Fr<EC_ppT> >;
template<typename EC_ppT>
using G1_vector = std::vector<G1<EC_ppT> >;
template<typename EC_ppT>
using G2_vector = std::vector<G2<EC_ppT> >;
} // libsnark
#endif // PUBLIC_PARAMS_HPP_

View File

@@ -0,0 +1,121 @@
/**
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "common/profiling.hpp"
#ifdef CURVE_BN128
#include "algebra/curves/bn128/bn128_pp.hpp"
#endif
#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp"
#include <gtest/gtest.h>
using namespace libsnark;
template<typename ppT>
void pairing_test()
{
GT<ppT> GT_one = GT<ppT>::one();
printf("Running bilinearity tests:\n");
G1<ppT> P = (Fr<ppT>::random_element()) * G1<ppT>::one();
//G1<ppT> P = Fr<ppT>("2") * G1<ppT>::one();
G2<ppT> Q = (Fr<ppT>::random_element()) * G2<ppT>::one();
//G2<ppT> Q = Fr<ppT>("3") * G2<ppT>::one();
printf("P:\n");
P.print();
P.print_coordinates();
printf("Q:\n");
Q.print();
Q.print_coordinates();
printf("\n\n");
Fr<ppT> s = Fr<ppT>::random_element();
//Fr<ppT> s = Fr<ppT>("2");
G1<ppT> sP = s * P;
G2<ppT> sQ = s * Q;
printf("Pairing bilinearity tests (three must match):\n");
GT<ppT> ans1 = ppT::reduced_pairing(sP, Q);
GT<ppT> ans2 = ppT::reduced_pairing(P, sQ);
GT<ppT> ans3 = ppT::reduced_pairing(P, Q)^s;
ans1.print();
ans2.print();
ans3.print();
EXPECT_EQ(ans1, ans2);
EXPECT_EQ(ans2, ans3);
EXPECT_NE(ans1, GT_one);
EXPECT_EQ((ans1^Fr<ppT>::field_char()), GT_one);
printf("\n\n");
}
template<typename ppT>
void double_miller_loop_test()
{
const G1<ppT> P1 = (Fr<ppT>::random_element()) * G1<ppT>::one();
const G1<ppT> P2 = (Fr<ppT>::random_element()) * G1<ppT>::one();
const G2<ppT> Q1 = (Fr<ppT>::random_element()) * G2<ppT>::one();
const G2<ppT> Q2 = (Fr<ppT>::random_element()) * G2<ppT>::one();
const G1_precomp<ppT> prec_P1 = ppT::precompute_G1(P1);
const G1_precomp<ppT> prec_P2 = ppT::precompute_G1(P2);
const G2_precomp<ppT> prec_Q1 = ppT::precompute_G2(Q1);
const G2_precomp<ppT> prec_Q2 = ppT::precompute_G2(Q2);
const Fqk<ppT> ans_1 = ppT::miller_loop(prec_P1, prec_Q1);
const Fqk<ppT> ans_2 = ppT::miller_loop(prec_P2, prec_Q2);
const Fqk<ppT> ans_12 = ppT::double_miller_loop(prec_P1, prec_Q1, prec_P2, prec_Q2);
EXPECT_EQ(ans_1 * ans_2, ans_12);
}
template<typename ppT>
void affine_pairing_test()
{
GT<ppT> GT_one = GT<ppT>::one();
printf("Running bilinearity tests:\n");
G1<ppT> P = (Fr<ppT>::random_element()) * G1<ppT>::one();
G2<ppT> Q = (Fr<ppT>::random_element()) * G2<ppT>::one();
printf("P:\n");
P.print();
printf("Q:\n");
Q.print();
printf("\n\n");
Fr<ppT> s = Fr<ppT>::random_element();
G1<ppT> sP = s * P;
G2<ppT> sQ = s * Q;
printf("Pairing bilinearity tests (three must match):\n");
GT<ppT> ans1 = ppT::affine_reduced_pairing(sP, Q);
GT<ppT> ans2 = ppT::affine_reduced_pairing(P, sQ);
GT<ppT> ans3 = ppT::affine_reduced_pairing(P, Q)^s;
ans1.print();
ans2.print();
ans3.print();
EXPECT_EQ(ans1, ans2);
EXPECT_EQ(ans2, ans3);
EXPECT_NE(ans1, GT_one);
EXPECT_EQ((ans1^Fr<ppT>::field_char()), GT_one);
printf("\n\n");
}
TEST(algebra, bilinearity)
{
start_profiling();
alt_bn128_pp::init_public_params();
pairing_test<alt_bn128_pp>();
double_miller_loop_test<alt_bn128_pp>();
#ifdef CURVE_BN128 // BN128 has fancy dependencies so it may be disabled
bn128_pp::init_public_params();
pairing_test<bn128_pp>();
double_miller_loop_test<bn128_pp>();
#endif
}

View File

@@ -0,0 +1,153 @@
/**
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "common/profiling.hpp"
#ifdef CURVE_BN128
#include "algebra/curves/bn128/bn128_pp.hpp"
#endif
#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp"
#include <sstream>
#include <gtest/gtest.h>
using namespace libsnark;
template<typename GroupT>
void test_mixed_add()
{
GroupT base, el, result;
base = GroupT::zero();
el = GroupT::zero();
el.to_special();
result = base.mixed_add(el);
EXPECT_EQ(result, base + el);
base = GroupT::zero();
el = GroupT::random_element();
el.to_special();
result = base.mixed_add(el);
EXPECT_EQ(result, base + el);
base = GroupT::random_element();
el = GroupT::zero();
el.to_special();
result = base.mixed_add(el);
EXPECT_EQ(result, base + el);
base = GroupT::random_element();
el = GroupT::random_element();
el.to_special();
result = base.mixed_add(el);
EXPECT_EQ(result, base + el);
base = GroupT::random_element();
el = base;
el.to_special();
result = base.mixed_add(el);
EXPECT_EQ(result, base.dbl());
}
template<typename GroupT>
void test_group()
{
bigint<1> rand1 = bigint<1>("76749407");
bigint<1> rand2 = bigint<1>("44410867");
bigint<1> randsum = bigint<1>("121160274");
GroupT zero = GroupT::zero();
EXPECT_EQ(zero, zero);
GroupT one = GroupT::one();
EXPECT_EQ(one, one);
GroupT two = bigint<1>(2l) * GroupT::one();
EXPECT_EQ(two, two);
GroupT five = bigint<1>(5l) * GroupT::one();
GroupT three = bigint<1>(3l) * GroupT::one();
GroupT four = bigint<1>(4l) * GroupT::one();
EXPECT_EQ(two+five, three+four);
GroupT a = GroupT::random_element();
GroupT b = GroupT::random_element();
EXPECT_NE(one, zero);
EXPECT_NE(a, zero);
EXPECT_NE(a, one);
EXPECT_NE(b, zero);
EXPECT_NE(b, one);
EXPECT_EQ(a.dbl(), a + a);
EXPECT_EQ(b.dbl(), b + b);
EXPECT_EQ(one.add(two), three);
EXPECT_EQ(two.add(one), three);
EXPECT_EQ(a + b, b + a);
EXPECT_EQ(a - a, zero);
EXPECT_EQ(a - b, a + (-b));
EXPECT_EQ(a - b, (-b) + a);
// handle special cases
EXPECT_EQ(zero + (-a), -a);
EXPECT_EQ(zero - a, -a);
EXPECT_EQ(a - zero, a);
EXPECT_EQ(a + zero, a);
EXPECT_EQ(zero + a, a);
EXPECT_EQ((a + b).dbl(), (a + b) + (b + a));
EXPECT_EQ(bigint<1>("2") * (a + b), (a + b) + (b + a));
EXPECT_EQ((rand1 * a) + (rand2 * a), (randsum * a));
EXPECT_EQ(GroupT::order() * a, zero);
EXPECT_EQ(GroupT::order() * one, zero);
EXPECT_NE((GroupT::order() * a) - a, zero);
EXPECT_NE((GroupT::order() * one) - one, zero);
test_mixed_add<GroupT>();
}
template<typename GroupT>
void test_mul_by_q()
{
GroupT a = GroupT::random_element();
EXPECT_EQ((GroupT::base_field_char()*a), a.mul_by_q());
}
template<typename GroupT>
void test_output()
{
GroupT g = GroupT::zero();
for (size_t i = 0; i < 1000; ++i)
{
std::stringstream ss;
ss << g;
GroupT gg;
ss >> gg;
EXPECT_EQ(g, gg);
/* use a random point in next iteration */
g = GroupT::random_element();
}
}
TEST(algebra, groups)
{
alt_bn128_pp::init_public_params();
test_group<G1<alt_bn128_pp> >();
test_output<G1<alt_bn128_pp> >();
test_group<G2<alt_bn128_pp> >();
test_output<G2<alt_bn128_pp> >();
test_mul_by_q<G2<alt_bn128_pp> >();
#ifdef CURVE_BN128 // BN128 has fancy dependencies so it may be disabled
bn128_pp::init_public_params();
test_group<G1<bn128_pp> >();
test_output<G1<bn128_pp> >();
test_group<G2<bn128_pp> >();
test_output<G2<bn128_pp> >();
#endif
}

View File

@@ -0,0 +1,45 @@
/** @file
*****************************************************************************
Declaration of interfaces for the "basic radix-2" evaluation domain.
Roughly, the domain has size m = 2^k and consists of the m-th roots of unity.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef BASIC_RADIX2_DOMAIN_HPP_
#define BASIC_RADIX2_DOMAIN_HPP_
#include "algebra/evaluation_domain/evaluation_domain.hpp"
namespace libsnark {
template<typename FieldT>
class basic_radix2_domain : public evaluation_domain<FieldT> {
public:
FieldT omega;
basic_radix2_domain(const size_t m);
void FFT(std::vector<FieldT> &a);
void iFFT(std::vector<FieldT> &a);
void cosetFFT(std::vector<FieldT> &a, const FieldT &g);
void icosetFFT(std::vector<FieldT> &a, const FieldT &g);
std::vector<FieldT> lagrange_coeffs(const FieldT &t);
FieldT get_element(const size_t idx);
FieldT compute_Z(const FieldT &t);
void add_poly_Z(const FieldT &coeff, std::vector<FieldT> &H);
void divide_by_Z_on_coset(std::vector<FieldT> &P);
};
} // libsnark
#include "algebra/evaluation_domain/domains/basic_radix2_domain.tcc"
#endif // BASIC_RADIX2_DOMAIN_HPP_

View File

@@ -0,0 +1,112 @@
/** @file
*****************************************************************************
Implementation of interfaces for the "basic radix-2" evaluation domain.
See basic_radix2_domain.hpp .
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef BASIC_RADIX2_DOMAIN_TCC_
#define BASIC_RADIX2_DOMAIN_TCC_
#include "algebra/evaluation_domain/domains/basic_radix2_domain_aux.hpp"
namespace libsnark {
template<typename FieldT>
basic_radix2_domain<FieldT>::basic_radix2_domain(const size_t m) : evaluation_domain<FieldT>(m)
{
assert(m > 1);
const size_t logm = log2(m);
assert(logm <= (FieldT::s));
omega = get_root_of_unity<FieldT>(m);
}
template<typename FieldT>
void basic_radix2_domain<FieldT>::FFT(std::vector<FieldT> &a)
{
enter_block("Execute FFT");
assert(a.size() == this->m);
_basic_radix2_FFT(a, omega);
leave_block("Execute FFT");
}
template<typename FieldT>
void basic_radix2_domain<FieldT>::iFFT(std::vector<FieldT> &a)
{
enter_block("Execute inverse FFT");
assert(a.size() == this->m);
_basic_radix2_FFT(a, omega.inverse());
const FieldT sconst = FieldT(a.size()).inverse();
for (size_t i = 0; i < a.size(); ++i)
{
a[i] *= sconst;
}
leave_block("Execute inverse FFT");
}
template<typename FieldT>
void basic_radix2_domain<FieldT>::cosetFFT(std::vector<FieldT> &a, const FieldT &g)
{
enter_block("Execute coset FFT");
_multiply_by_coset(a, g);
FFT(a);
leave_block("Execute coset FFT");
}
template<typename FieldT>
void basic_radix2_domain<FieldT>::icosetFFT(std::vector<FieldT> &a, const FieldT &g)
{
enter_block("Execute inverse coset IFFT");
iFFT(a);
_multiply_by_coset(a, g.inverse());
leave_block("Execute inverse coset IFFT");
}
template<typename FieldT>
std::vector<FieldT> basic_radix2_domain<FieldT>::lagrange_coeffs(const FieldT &t)
{
return _basic_radix2_lagrange_coeffs(this->m, t);
}
template<typename FieldT>
FieldT basic_radix2_domain<FieldT>::get_element(const size_t idx)
{
return omega^idx;
}
template<typename FieldT>
FieldT basic_radix2_domain<FieldT>::compute_Z(const FieldT &t)
{
return (t^this->m) - FieldT::one();
}
template<typename FieldT>
void basic_radix2_domain<FieldT>::add_poly_Z(const FieldT &coeff, std::vector<FieldT> &H)
{
assert(H.size() == this->m+1);
H[this->m] += coeff;
H[0] -= coeff;
}
template<typename FieldT>
void basic_radix2_domain<FieldT>::divide_by_Z_on_coset(std::vector<FieldT> &P)
{
const FieldT coset = FieldT::multiplicative_generator;
const FieldT Z_inverse_at_coset = this->compute_Z(coset).inverse();
for (size_t i = 0; i < this->m; ++i)
{
P[i] *= Z_inverse_at_coset;
}
}
} // libsnark
#endif // BASIC_RADIX2_DOMAIN_TCC_

View File

@@ -0,0 +1,48 @@
/** @file
*****************************************************************************
Declaration of interfaces for auxiliary functions for the "basic radix-2" evaluation domain.
These functions compute the radix-2 FFT (in single- or multi-thread mode) and,
also compute Lagrange coefficients.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef BASIC_RADIX2_DOMAIN_AUX_HPP_
#define BASIC_RADIX2_DOMAIN_AUX_HPP_
namespace libsnark {
/**
* Compute the radix-2 FFT of the vector a over the set S={omega^{0},...,omega^{m-1}}.
*/
template<typename FieldT>
void _basic_radix2_FFT(std::vector<FieldT> &a, const FieldT &omega);
/**
* A multi-thread version of _basic_radix2_FFT.
*/
template<typename FieldT>
void _parallel_basic_radix2_FFT(std::vector<FieldT> &a, const FieldT &omega);
/**
* Translate the vector a to a coset defined by g.
*/
template<typename FieldT>
void _multiply_by_coset(std::vector<FieldT> &a, const FieldT &g);
/**
* Compute the m Lagrange coefficients, relative to the set S={omega^{0},...,omega^{m-1}}, at the field element t.
*/
template<typename FieldT>
std::vector<FieldT> _basic_radix2_lagrange_coeffs(const size_t m, const FieldT &t);
} // libsnark
#include "algebra/evaluation_domain/domains/basic_radix2_domain_aux.tcc"
#endif // BASIC_RADIX2_DOMAIN_AUX_HPP_

View File

@@ -0,0 +1,242 @@
/** @file
*****************************************************************************
Implementation of interfaces for auxiliary functions for the "basic radix-2" evaluation domain.
See basic_radix2_domain_aux.hpp .
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef BASIC_RADIX2_DOMAIN_AUX_TCC_
#define BASIC_RADIX2_DOMAIN_AUX_TCC_
#include <cassert>
#ifdef MULTICORE
#include <omp.h>
#endif
#include "algebra/fields/field_utils.hpp"
#include "common/profiling.hpp"
#include "common/utils.hpp"
namespace libsnark {
#ifdef MULTICORE
#define _basic_radix2_FFT _basic_parallel_radix2_FFT
#else
#define _basic_radix2_FFT _basic_serial_radix2_FFT
#endif
/*
Below we make use of pseudocode from [CLRS 2n Ed, pp. 864].
Also, note that it's the caller's responsibility to multiply by 1/N.
*/
template<typename FieldT>
void _basic_serial_radix2_FFT(std::vector<FieldT> &a, const FieldT &omega)
{
const size_t n = a.size(), logn = log2(n);
assert(n == (1u << logn));
/* swapping in place (from Storer's book) */
for (size_t k = 0; k < n; ++k)
{
const size_t rk = bitreverse(k, logn);
if (k < rk)
std::swap(a[k], a[rk]);
}
size_t m = 1; // invariant: m = 2^{s-1}
for (size_t s = 1; s <= logn; ++s)
{
// w_m is 2^s-th root of unity now
const FieldT w_m = omega^(n/(2*m));
asm volatile ("/* pre-inner */");
for (size_t k = 0; k < n; k += 2*m)
{
FieldT w = FieldT::one();
for (size_t j = 0; j < m; ++j)
{
const FieldT t = w * a[k+j+m];
a[k+j+m] = a[k+j] - t;
a[k+j] += t;
w *= w_m;
}
}
asm volatile ("/* post-inner */");
m *= 2;
}
}
template<typename FieldT>
void _basic_parallel_radix2_FFT_inner(std::vector<FieldT> &a, const FieldT &omega, const size_t log_cpus)
{
const size_t num_cpus = 1ul<<log_cpus;
const size_t m = a.size();
const size_t log_m = log2(m);
assert(m == 1ul<<log_m);
if (log_m < log_cpus)
{
_basic_serial_radix2_FFT(a, omega);
return;
}
enter_block("Shuffle inputs");
std::vector<std::vector<FieldT> > tmp(num_cpus);
for (size_t j = 0; j < num_cpus; ++j)
{
tmp[j].resize(1ul<<(log_m-log_cpus), FieldT::zero());
}
#ifdef MULTICORE
#pragma omp parallel for
#endif
for (size_t j = 0; j < num_cpus; ++j)
{
const FieldT omega_j = omega^j;
const FieldT omega_step = omega^(j<<(log_m - log_cpus));
FieldT elt = FieldT::one();
for (size_t i = 0; i < 1ul<<(log_m - log_cpus); ++i)
{
for (size_t s = 0; s < num_cpus; ++s)
{
// invariant: elt is omega^(j*idx)
const size_t idx = (i + (s<<(log_m - log_cpus))) % (1u << log_m);
tmp[j][i] += a[idx] * elt;
elt *= omega_step;
}
elt *= omega_j;
}
}
leave_block("Shuffle inputs");
enter_block("Execute sub-FFTs");
const FieldT omega_num_cpus = omega^num_cpus;
#ifdef MULTICORE
#pragma omp parallel for
#endif
for (size_t j = 0; j < num_cpus; ++j)
{
_basic_serial_radix2_FFT(tmp[j], omega_num_cpus);
}
leave_block("Execute sub-FFTs");
enter_block("Re-shuffle outputs");
#ifdef MULTICORE
#pragma omp parallel for
#endif
for (size_t i = 0; i < num_cpus; ++i)
{
for (size_t j = 0; j < 1ul<<(log_m - log_cpus); ++j)
{
// now: i = idx >> (log_m - log_cpus) and j = idx % (1u << (log_m - log_cpus)), for idx = ((i<<(log_m-log_cpus))+j) % (1u << log_m)
a[(j<<log_cpus) + i] = tmp[i][j];
}
}
leave_block("Re-shuffle outputs");
}
template<typename FieldT>
void _basic_parallel_radix2_FFT(std::vector<FieldT> &a, const FieldT &omega)
{
#ifdef MULTICORE
const size_t num_cpus = omp_get_max_threads();
#else
const size_t num_cpus = 1;
#endif
const size_t log_cpus = ((num_cpus & (num_cpus - 1)) == 0 ? log2(num_cpus) : log2(num_cpus) - 1);
#ifdef DEBUG
print_indent(); printf("* Invoking parallel FFT on 2^%zu CPUs (omp_get_max_threads = %zu)\n", log_cpus, num_cpus);
#endif
if (log_cpus == 0)
{
_basic_serial_radix2_FFT(a, omega);
}
else
{
_basic_parallel_radix2_FFT_inner(a, omega, log_cpus);
}
}
template<typename FieldT>
void _multiply_by_coset(std::vector<FieldT> &a, const FieldT &g)
{
//enter_block("Multiply by coset");
FieldT u = g;
for (size_t i = 1; i < a.size(); ++i)
{
a[i] *= u;
u *= g;
}
//leave_block("Multiply by coset");
}
template<typename FieldT>
std::vector<FieldT> _basic_radix2_lagrange_coeffs(const size_t m, const FieldT &t)
{
if (m == 1)
{
return std::vector<FieldT>(1, FieldT::one());
}
assert(m == (1u << log2(m)));
const FieldT omega = get_root_of_unity<FieldT>(m);
std::vector<FieldT> u(m, FieldT::zero());
/*
If t equals one of the roots of unity in S={omega^{0},...,omega^{m-1}}
then output 1 at the right place, and 0 elsewhere
*/
if ((t^m) == (FieldT::one()))
{
FieldT omega_i = FieldT::one();
for (size_t i = 0; i < m; ++i)
{
if (omega_i == t) // i.e., t equals omega^i
{
u[i] = FieldT::one();
return u;
}
omega_i *= omega;
}
}
/*
Otherwise, if t does not equal any of the roots of unity in S,
then compute each L_{i,S}(t) as Z_{S}(t) * v_i / (t-\omega^i)
where:
- Z_{S}(t) = \prod_{j} (t-\omega^j) = (t^m-1), and
- v_{i} = 1 / \prod_{j \neq i} (\omega^i-\omega^j).
Below we use the fact that v_{0} = 1/m and v_{i+1} = \omega * v_{i}.
*/
const FieldT Z = (t^m)-FieldT::one();
FieldT l = Z * FieldT(m).inverse();
FieldT r = FieldT::one();
for (size_t i = 0; i < m; ++i)
{
u[i] = l * (t - r).inverse();
l *= omega;
r *= omega;
}
return u;
}
} // libsnark
#endif // BASIC_RADIX2_DOMAIN_AUX_TCC_

View File

@@ -0,0 +1,125 @@
/** @file
*****************************************************************************
Declaration of interfaces for evaluation domains.
Roughly, given a desired size m for the domain, the constructor selects
a choice of domain S with size ~m that has been selected so to optimize
- computations of Lagrange polynomials, and
- FFT/iFFT computations.
An evaluation domain also provides other other functions, e.g., accessing
individual elements in S or evaluating its vanishing polynomial.
The descriptions below make use of the definition of a *Lagrange polynomial*,
which we recall. Given a field F, a subset S=(a_i)_i of F, and an index idx
in {0,...,|S-1|}, the idx-th Lagrange polynomial (wrt to subset S) is defined to be
\f[ L_{idx,S}(z) := prod_{k \neq idx} (z - a_k) / prod_{k \neq idx} (a_{idx} - a_k) \f]
Note that, by construction:
\f[ \forall j \neq idx: L_{idx,S}(a_{idx}) = 1 \text{ and } L_{idx,S}(a_j) = 0 \f]
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef EVALUATION_DOMAIN_HPP_
#define EVALUATION_DOMAIN_HPP_
#include <memory>
namespace libsnark {
/**
* An evaluation domain.
*/
template<typename FieldT>
class evaluation_domain {
public:
const size_t m;
/**
* Construct an evaluation domain S of size m, if possible.
*
* (See the function get_evaluation_domain below.)
*/
evaluation_domain(const size_t m) : m(m) {};
/**
* Get the idx-th element in S.
*/
virtual FieldT get_element(const size_t idx) = 0;
/**
* Compute the FFT, over the domain S, of the vector a.
*/
virtual void FFT(std::vector<FieldT> &a) = 0;
/**
* Compute the inverse FFT, over the domain S, of the vector a.
*/
virtual void iFFT(std::vector<FieldT> &a) = 0;
/**
* Compute the FFT, over the domain g*S, of the vector a.
*/
virtual void cosetFFT(std::vector<FieldT> &a, const FieldT &g) = 0;
/**
* Compute the inverse FFT, over the domain g*S, of the vector a.
*/
virtual void icosetFFT(std::vector<FieldT> &a, const FieldT &g) = 0;
/**
* Evaluate all Lagrange polynomials.
*
* The inputs are:
* - an integer m
* - an element t
* The output is a vector (b_{0},...,b_{m-1})
* where b_{i} is the evaluation of L_{i,S}(z) at z = t.
*/
virtual std::vector<FieldT> lagrange_coeffs(const FieldT &t) = 0;
/**
* Evaluate the vanishing polynomial of S at the field element t.
*/
virtual FieldT compute_Z(const FieldT &t) = 0;
/**
* Add the coefficients of the vanishing polynomial of S to the coefficients of the polynomial H.
*/
virtual void add_poly_Z(const FieldT &coeff, std::vector<FieldT> &H) = 0;
/**
* Multiply by the evaluation, on a coset of S, of the inverse of the vanishing polynomial of S.
*/
virtual void divide_by_Z_on_coset(std::vector<FieldT> &P) = 0;
};
/**
* Return an evaluation domain object in which the domain S has size |S| >= min_size.
* The function chooses from different supported domains, depending on min_size.
*/
template<typename FieldT>
std::shared_ptr<evaluation_domain<FieldT> > get_evaluation_domain(const size_t min_size);
/**
* Naive evaluation of a *single* Lagrange polynomial, used for testing purposes.
*
* The inputs are:
* - an integer m
* - a domain S = (a_{0},...,a_{m-1}) of size m
* - a field element element t
* - an index idx in {0,...,m-1}
* The output is the polynomial L_{idx,S}(z) evaluated at z = t.
*/
template<typename FieldT>
FieldT lagrange_eval(const size_t m, const std::vector<FieldT> &domain, const FieldT &t, const size_t idx);
} // libsnark
#include "algebra/evaluation_domain/evaluation_domain.tcc"
#endif // EVALUATION_DOMAIN_HPP_

View File

@@ -0,0 +1,117 @@
/** @file
*****************************************************************************
Imeplementation of interfaces for evaluation domains.
See evaluation_domain.hpp .
We currently implement, and select among, three types of domains:
- "basic radix-2": the domain has size m = 2^k and consists of the m-th roots of unity
- "extended radix-2": the domain has size m = 2^{k+1} and consists of "the m-th roots of unity" union "a coset"
- "step radix-2": the domain has size m = 2^k + 2^r and consists of "the 2^k-th roots of unity" union "a coset of 2^r-th roots of unity"
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef EVALUATION_DOMAIN_TCC_
#define EVALUATION_DOMAIN_TCC_
#include <cassert>
#include "algebra/fields/field_utils.hpp"
#include "algebra/evaluation_domain/domains/basic_radix2_domain.hpp"
namespace libsnark {
template<typename FieldT>
std::shared_ptr<evaluation_domain<FieldT> > get_evaluation_domain(const size_t min_size)
{
assert(min_size > 1);
const size_t log_min_size = log2(min_size);
assert(log_min_size <= (FieldT::s+1));
std::shared_ptr<evaluation_domain<FieldT> > result;
if (min_size == (1u << log_min_size))
{
if (log_min_size == FieldT::s+1)
{
if (!inhibit_profiling_info)
{
print_indent(); printf("* Selected domain: extended_radix2\n");
}
assert(0);
}
else
{
if (!inhibit_profiling_info)
{
print_indent(); printf("* Selected domain: basic_radix2\n");
}
result.reset(new basic_radix2_domain<FieldT>(min_size));
}
}
else
{
const size_t big = 1ul<<(log2(min_size)-1);
const size_t small = min_size - big;
const size_t rounded_small = (1ul<<log2(small));
if (big == rounded_small)
{
if (log2(big + rounded_small) < FieldT::s+1)
{
if (!inhibit_profiling_info)
{
print_indent(); printf("* Selected domain: basic_radix2\n");
}
result.reset(new basic_radix2_domain<FieldT>(big + rounded_small));
}
else
{
if (!inhibit_profiling_info)
{
print_indent(); printf("* Selected domain: extended_radix2\n");
}
assert(0);
}
}
else
{
if (!inhibit_profiling_info)
{
print_indent(); printf("* Selected domain: step_radix2\n");
}
assert(0);
}
}
return result;
}
template<typename FieldT>
FieldT lagrange_eval(const size_t m, const std::vector<FieldT> &domain, const FieldT &t, const size_t idx)
{
assert(m == domain.size());
assert(idx < m);
FieldT num = FieldT::one();
FieldT denom = FieldT::one();
for (size_t k = 0; k < m; ++k)
{
if (k == idx)
{
continue;
}
num *= t - domain[k];
denom *= domain[idx] - domain[k];
}
return num * denom.inverse();
}
} // libsnark
#endif // EVALUATION_DOMAIN_TCC_

View File

@@ -0,0 +1,31 @@
/** @file
*****************************************************************************
Declaration of interfaces for (square-and-multiply) exponentiation.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef EXPONENTIATION_HPP_
#define EXPONENTIATION_HPP_
#include <cstdint>
#include "algebra/fields/bigint.hpp"
namespace libsnark {
template<typename FieldT, mp_size_t m>
FieldT power(const FieldT &base, const bigint<m> &exponent);
template<typename FieldT>
FieldT power(const FieldT &base, const unsigned long exponent);
} // libsnark
#include "algebra/exponentiation/exponentiation.tcc"
#endif // EXPONENTIATION_HPP_

View File

@@ -0,0 +1,53 @@
/** @file
*****************************************************************************
Implementation of interfaces for (square-and-multiply) exponentiation.
See exponentiation.hpp .
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef EXPONENTIATION_TCC_
#define EXPONENTIATION_TCC_
#include "common/utils.hpp"
namespace libsnark {
template<typename FieldT, mp_size_t m>
FieldT power(const FieldT &base, const bigint<m> &exponent)
{
FieldT result = FieldT::one();
bool found_one = false;
for (long i = exponent.max_bits() - 1; i >= 0; --i)
{
if (found_one)
{
result = result * result;
}
if (exponent.test_bit(i))
{
found_one = true;
result = result * base;
}
}
return result;
}
template<typename FieldT>
FieldT power(const FieldT &base, const unsigned long exponent)
{
return power<FieldT>(base, bigint<1>(exponent));
}
} // libsnark
#endif // EXPONENTIATION_TCC_

View File

@@ -0,0 +1,70 @@
/** @file
*****************************************************************************
Declaration of bigint wrapper class around GMP's MPZ long integers.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef BIGINT_HPP_
#define BIGINT_HPP_
#include <cstddef>
#include <iostream>
#include <gmp.h>
#include "common/serialization.hpp"
namespace libsnark {
template<mp_size_t n> class bigint;
template<mp_size_t n> std::ostream& operator<<(std::ostream &, const bigint<n>&);
template<mp_size_t n> std::istream& operator>>(std::istream &, bigint<n>&);
/**
* Wrapper class around GMP's MPZ long integers. It supports arithmetic operations,
* serialization and randomization. Serialization is fragile, see common/serialization.hpp.
*/
template<mp_size_t n>
class bigint {
public:
static const mp_size_t N = n;
mp_limb_t data[n] = {0};
bigint() = default;
bigint(const unsigned long x); /// Initalize from a small integer
bigint(const char* s); /// Initialize from a string containing an integer in decimal notation
bigint(const mpz_t r); /// Initialize from MPZ element
void print() const;
void print_hex() const;
bool operator==(const bigint<n>& other) const;
bool operator!=(const bigint<n>& other) const;
void clear();
bool is_zero() const;
size_t max_bits() const { return n * GMP_NUMB_BITS; }
size_t num_bits() const;
unsigned long as_ulong() const; /* return the last limb of the integer */
void to_mpz(mpz_t r) const;
bool test_bit(const std::size_t bitno) const;
template<mp_size_t m> inline void operator+=(const bigint<m>& other);
template<mp_size_t m> inline bigint<n+m> operator*(const bigint<m>& other) const;
template<mp_size_t d> static inline void div_qr(bigint<n-d+1>& quotient, bigint<d>& remainder,
const bigint<n>& dividend, const bigint<d>& divisor);
template<mp_size_t m> inline bigint<m> shorten(const bigint<m>& q, const char *msg) const;
inline void limit(const bigint<n>& q, const char *msg) const;
bool operator>(const bigint<n>& other) const;
bigint& randomize();
friend std::ostream& operator<< <n>(std::ostream &out, const bigint<n> &b);
friend std::istream& operator>> <n>(std::istream &in, bigint<n> &b);
};
} // libsnark
#include "algebra/fields/bigint.tcc"
#endif

View File

@@ -0,0 +1,278 @@
/** @file
*****************************************************************************
Implementation of bigint wrapper class around GMP's MPZ long integers.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef BIGINT_TCC_
#define BIGINT_TCC_
#include <cassert>
#include <climits>
#include <cstring>
#include "sodium.h"
namespace libsnark {
template<mp_size_t n>
bigint<n>::bigint(const unsigned long x) /// Initalize from a small integer
{
static_assert(ULONG_MAX <= GMP_NUMB_MAX, "unsigned long does not fit in a GMP limb");
this->data[0] = x;
}
template<mp_size_t n>
bigint<n>::bigint(const char* s) /// Initialize from a string containing an integer in decimal notation
{
size_t l = strlen(s);
unsigned char* s_copy = new unsigned char[l];
for (size_t i = 0; i < l; ++i)
{
assert(s[i] >= '0' && s[i] <= '9');
s_copy[i] = s[i] - '0';
}
mp_size_t limbs_written = mpn_set_str(this->data, s_copy, l, 10);
assert(limbs_written <= n);
delete[] s_copy;
}
template<mp_size_t n>
bigint<n>::bigint(const mpz_t r) /// Initialize from MPZ element
{
mpz_t k;
mpz_init_set(k, r);
for (size_t i = 0; i < n; ++i)
{
data[i] = mpz_get_ui(k);
mpz_fdiv_q_2exp(k, k, GMP_NUMB_BITS);
}
assert(mpz_sgn(k) == 0);
mpz_clear(k);
}
template<mp_size_t n>
void bigint<n>::print() const
{
gmp_printf("%Nd\n", this->data, n);
}
template<mp_size_t n>
void bigint<n>::print_hex() const
{
gmp_printf("%Nx\n", this->data, n);
}
template<mp_size_t n>
bool bigint<n>::operator==(const bigint<n>& other) const
{
return (mpn_cmp(this->data, other.data, n) == 0);
}
template<mp_size_t n>
bool bigint<n>::operator!=(const bigint<n>& other) const
{
return !(operator==(other));
}
template<mp_size_t n>
void bigint<n>::clear()
{
mpn_zero(this->data, n);
}
template<mp_size_t n>
bool bigint<n>::is_zero() const
{
for (mp_size_t i = 0; i < n; ++i)
{
if (this->data[i])
{
return false;
}
}
return true;
}
template<mp_size_t n>
size_t bigint<n>::num_bits() const
{
/*
for (long i = max_bits(); i >= 0; --i)
{
if (this->test_bit(i))
{
return i+1;
}
}
return 0;
*/
for (long i = n-1; i >= 0; --i)
{
mp_limb_t x = this->data[i];
if (x == 0)
{
continue;
}
else
{
return ((i+1) * GMP_NUMB_BITS) - __builtin_clzl(x);
}
}
return 0;
}
template<mp_size_t n>
unsigned long bigint<n>::as_ulong() const
{
return this->data[0];
}
template<mp_size_t n>
void bigint<n>::to_mpz(mpz_t r) const
{
mpz_set_ui(r, 0);
for (int i = n-1; i >= 0; --i)
{
mpz_mul_2exp(r, r, GMP_NUMB_BITS);
mpz_add_ui(r, r, this->data[i]);
}
}
template<mp_size_t n>
bool bigint<n>::test_bit(const std::size_t bitno) const
{
if (bitno >= n * GMP_NUMB_BITS)
{
return false;
}
else
{
const std::size_t part = bitno/GMP_NUMB_BITS;
const std::size_t bit = bitno - (GMP_NUMB_BITS*part);
const mp_limb_t one = 1;
return (this->data[part] & (one<<bit));
}
}
template<mp_size_t n> template<mp_size_t m>
inline void bigint<n>::operator+=(const bigint<m>& other)
{
static_assert(n >= m, "first arg must not be smaller than second arg for bigint in-place add");
mpn_add(data, data, n, other.data, m);
}
template<mp_size_t n> template<mp_size_t m>
inline bigint<n+m> bigint<n>::operator*(const bigint<m>& other) const
{
static_assert(n >= m, "first arg must not be smaller than second arg for bigint mul");
bigint<n+m> res;
mpn_mul(res.data, data, n, other.data, m);
return res;
}
template<mp_size_t n> template<mp_size_t d>
inline void bigint<n>::div_qr(bigint<n-d+1>& quotient, bigint<d>& remainder,
const bigint<n>& dividend, const bigint<d>& divisor)
{
static_assert(n >= d, "dividend must not be smaller than divisor for bigint::div_qr");
assert(divisor.data[d-1] != 0);
mpn_tdiv_qr(quotient.data, remainder.data, 0, dividend.data, n, divisor.data, d);
}
// Return a copy shortened to m limbs provided it is less than limit, throwing std::domain_error if not in range.
template<mp_size_t n> template<mp_size_t m>
inline bigint<m> bigint<n>::shorten(const bigint<m>& q, const char *msg) const
{
static_assert(m <= n, "number of limbs must not increase for bigint::shorten");
for (mp_size_t i = m; i < n; i++) { // high-order limbs
if (data[i] != 0) {
throw std::domain_error(msg);
}
}
bigint<m> res;
mpn_copyi(res.data, data, m);
res.limit(q, msg);
return res;
}
template<mp_size_t n>
inline void bigint<n>::limit(const bigint<n>& q, const char *msg) const
{
if (!(q > *this)) {
throw std::domain_error(msg);
}
}
template<mp_size_t n>
inline bool bigint<n>::operator>(const bigint<n>& other) const
{
return mpn_cmp(this->data, other.data, n) > 0;
}
template<mp_size_t n>
bigint<n>& bigint<n>::randomize()
{
assert(GMP_NUMB_BITS == sizeof(mp_limb_t) * 8);
randombytes_buf(this->data, sizeof(mp_limb_t) * n);
return (*this);
}
template<mp_size_t n>
std::ostream& operator<<(std::ostream &out, const bigint<n> &b)
{
#ifdef BINARY_OUTPUT
out.write((char*)b.data, sizeof(b.data[0]) * n);
#else
mpz_t t;
mpz_init(t);
b.to_mpz(t);
out << t;
mpz_clear(t);
#endif
return out;
}
template<mp_size_t n>
std::istream& operator>>(std::istream &in, bigint<n> &b)
{
#ifdef BINARY_OUTPUT
in.read((char*)b.data, sizeof(b.data[0]) * n);
#else
std::string s;
in >> s;
size_t l = s.size();
unsigned char* s_copy = new unsigned char[l];
for (size_t i = 0; i < l; ++i)
{
assert(s[i] >= '0' && s[i] <= '9');
s_copy[i] = s[i] - '0';
}
mp_size_t limbs_written = mpn_set_str(b.data, s_copy, l, 10);
assert(limbs_written <= n);
delete[] s_copy;
#endif
return in;
}
} // libsnark
#endif // BIGINT_TCC_

View File

@@ -0,0 +1,51 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef FIELD_UTILS_HPP_
#define FIELD_UTILS_HPP_
#include <cstdint>
#include "common/utils.hpp"
#include "algebra/fields/bigint.hpp"
namespace libsnark {
// returns root of unity of order n (for n a power of 2), if one exists
template<typename FieldT>
FieldT get_root_of_unity(const size_t n);
template<typename FieldT>
std::vector<FieldT> pack_int_vector_into_field_element_vector(const std::vector<size_t> &v, const size_t w);
template<typename FieldT>
std::vector<FieldT> pack_bit_vector_into_field_element_vector(const bit_vector &v, const size_t chunk_bits);
template<typename FieldT>
std::vector<FieldT> pack_bit_vector_into_field_element_vector(const bit_vector &v);
template<typename FieldT>
std::vector<FieldT> convert_bit_vector_to_field_element_vector(const bit_vector &v);
template<typename FieldT>
bit_vector convert_field_element_vector_to_bit_vector(const std::vector<FieldT> &v);
template<typename FieldT>
bit_vector convert_field_element_to_bit_vector(const FieldT &el);
template<typename FieldT>
bit_vector convert_field_element_to_bit_vector(const FieldT &el, const size_t bitcount);
template<typename FieldT>
FieldT convert_bit_vector_to_field_element(const bit_vector &v);
template<typename FieldT>
void batch_invert(std::vector<FieldT> &vec);
} // libsnark
#include "algebra/fields/field_utils.tcc"
#endif // FIELD_UTILS_HPP_

View File

@@ -0,0 +1,183 @@
/** @file
*****************************************************************************
Implementation of misc. math and serialization utility functions
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef FIELD_UTILS_TCC_
#define FIELD_UTILS_TCC_
#include "common/utils.hpp"
namespace libsnark {
template<typename FieldT>
FieldT coset_shift()
{
return FieldT::multiplicative_generator.squared();
}
template<typename FieldT>
FieldT get_root_of_unity(const size_t n)
{
const size_t logn = log2(n);
assert(n == (1u << logn));
assert(logn <= FieldT::s);
FieldT omega = FieldT::root_of_unity;
for (size_t i = FieldT::s; i > logn; --i)
{
omega *= omega;
}
return omega;
}
template<typename FieldT>
std::vector<FieldT> pack_int_vector_into_field_element_vector(const std::vector<size_t> &v, const size_t w)
{
const size_t chunk_bits = FieldT::capacity();
const size_t repacked_size = div_ceil(v.size() * w, chunk_bits);
std::vector<FieldT> result(repacked_size);
for (size_t i = 0; i < repacked_size; ++i)
{
bigint<FieldT::num_limbs> b;
for (size_t j = 0; j < chunk_bits; ++j)
{
const size_t word_index = (i * chunk_bits + j) / w;
const size_t pos_in_word = (i * chunk_bits + j) % w;
const size_t word_or_0 = (word_index < v.size() ? v[word_index] : 0);
const size_t bit = (word_or_0 >> pos_in_word) & 1;
b.data[j / GMP_NUMB_BITS] |= bit << (j % GMP_NUMB_BITS);
}
result[i] = FieldT(b);
}
return result;
}
template<typename FieldT>
std::vector<FieldT> pack_bit_vector_into_field_element_vector(const bit_vector &v, const size_t chunk_bits)
{
assert(chunk_bits <= FieldT::capacity());
const size_t repacked_size = div_ceil(v.size(), chunk_bits);
std::vector<FieldT> result(repacked_size);
for (size_t i = 0; i < repacked_size; ++i)
{
bigint<FieldT::num_limbs> b;
for (size_t j = 0; j < chunk_bits; ++j)
{
b.data[j / GMP_NUMB_BITS] |= ((i * chunk_bits + j) < v.size() && v[i * chunk_bits + j] ? 1ll : 0ll) << (j % GMP_NUMB_BITS);
}
result[i] = FieldT(b);
}
return result;
}
template<typename FieldT>
std::vector<FieldT> pack_bit_vector_into_field_element_vector(const bit_vector &v)
{
return pack_bit_vector_into_field_element_vector<FieldT>(v, FieldT::capacity());
}
template<typename FieldT>
std::vector<FieldT> convert_bit_vector_to_field_element_vector(const bit_vector &v)
{
std::vector<FieldT> result;
result.reserve(v.size());
for (const bool b : v)
{
result.emplace_back(b ? FieldT::one() : FieldT::zero());
}
return result;
}
template<typename FieldT>
bit_vector convert_field_element_vector_to_bit_vector(const std::vector<FieldT> &v)
{
bit_vector result;
for (const FieldT &el : v)
{
const bit_vector el_bits = convert_field_element_to_bit_vector<FieldT>(el);
result.insert(result.end(), el_bits.begin(), el_bits.end());
}
return result;
}
template<typename FieldT>
bit_vector convert_field_element_to_bit_vector(const FieldT &el)
{
bit_vector result;
bigint<FieldT::num_limbs> b = el.as_bigint();
for (size_t i = 0; i < FieldT::size_in_bits(); ++i)
{
result.push_back(b.test_bit(i));
}
return result;
}
template<typename FieldT>
bit_vector convert_field_element_to_bit_vector(const FieldT &el, const size_t bitcount)
{
bit_vector result = convert_field_element_to_bit_vector(el);
result.resize(bitcount);
return result;
}
template<typename FieldT>
FieldT convert_bit_vector_to_field_element(const bit_vector &v)
{
assert(v.size() <= FieldT::size_in_bits());
FieldT res = FieldT::zero();
FieldT c = FieldT::one();
for (bool b : v)
{
res += b ? c : FieldT::zero();
c += c;
}
return res;
}
template<typename FieldT>
void batch_invert(std::vector<FieldT> &vec)
{
std::vector<FieldT> prod;
prod.reserve(vec.size());
FieldT acc = FieldT::one();
for (auto el : vec)
{
assert(!el.is_zero());
prod.emplace_back(acc);
acc = acc * el;
}
FieldT acc_inverse = acc.inverse();
for (long i = vec.size()-1; i >= 0; --i)
{
const FieldT old_el = vec[i];
vec[i] = acc_inverse * prod[i];
acc_inverse = acc_inverse * old_el;
}
}
} // libsnark
#endif // FIELD_UTILS_TCC_

View File

@@ -0,0 +1,182 @@
/** @file
*****************************************************************************
Declaration of arithmetic in the finite field F[p], for prime p of fixed length.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef FP_HPP_
#define FP_HPP_
#include "algebra/fields/bigint.hpp"
#include "algebra/exponentiation/exponentiation.hpp"
namespace libsnark {
template<mp_size_t n, const bigint<n>& modulus>
class Fp_model;
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream &, const Fp_model<n, modulus>&);
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream &, Fp_model<n, modulus> &);
/**
* Arithmetic in the finite field F[p], for prime p of fixed length.
*
* This class implements Fp-arithmetic, for a large prime p, using a fixed number
* of words. It is optimized for tight memory consumption, so the modulus p is
* passed as a template parameter, to avoid per-element overheads.
*
* The implementation is mostly a wrapper around GMP's MPN (constant-size integers).
* But for the integer sizes of interest for libsnark (3 to 5 limbs of 64 bits each),
* we implement performance-critical routines, like addition and multiplication,
* using hand-optimzied assembly code.
*/
template<mp_size_t n, const bigint<n>& modulus>
class Fp_model {
public:
bigint<n> mont_repr;
public:
static const mp_size_t num_limbs = n;
static const constexpr bigint<n>& mod = modulus;
#ifdef PROFILE_OP_COUNTS
static long long add_cnt;
static long long sub_cnt;
static long long mul_cnt;
static long long sqr_cnt;
static long long inv_cnt;
#endif
static size_t num_bits;
static bigint<n> euler; // (modulus-1)/2
static size_t s; // modulus = 2^s * t + 1
static bigint<n> t; // with t odd
static bigint<n> t_minus_1_over_2; // (t-1)/2
static Fp_model<n, modulus> nqr; // a quadratic nonresidue
static Fp_model<n, modulus> nqr_to_t; // nqr^t
static Fp_model<n, modulus> multiplicative_generator; // generator of Fp^*
static Fp_model<n, modulus> root_of_unity; // generator^((modulus-1)/2^s)
static mp_limb_t inv; // modulus^(-1) mod W, where W = 2^(word size)
static bigint<n> Rsquared; // R^2, where R = W^k, where k = ??
static bigint<n> Rcubed; // R^3
static bool modulus_is_valid() { return modulus.data[n-1] != 0; } // mpn inverse assumes that highest limb is non-zero
Fp_model() {};
Fp_model(const bigint<n> &b);
Fp_model(const long x, const bool is_unsigned=false);
void set_ulong(const unsigned long x);
void mul_reduce(const bigint<n> &other);
void clear();
/* Return the standard (not Montgomery) representation of the
Field element's requivalence class. I.e. Fp(2).as_bigint()
would return bigint(2) */
bigint<n> as_bigint() const;
/* Return the last limb of the standard representation of the
field element. E.g. on 64-bit architectures Fp(123).as_ulong()
and Fp(2^64+123).as_ulong() would both return 123. */
unsigned long as_ulong() const;
bool operator==(const Fp_model& other) const;
bool operator!=(const Fp_model& other) const;
bool is_zero() const;
void print() const;
Fp_model& operator+=(const Fp_model& other);
Fp_model& operator-=(const Fp_model& other);
Fp_model& operator*=(const Fp_model& other);
Fp_model& operator^=(const unsigned long pow);
template<mp_size_t m>
Fp_model& operator^=(const bigint<m> &pow);
Fp_model operator+(const Fp_model& other) const;
Fp_model operator-(const Fp_model& other) const;
Fp_model operator*(const Fp_model& other) const;
Fp_model operator-() const;
Fp_model squared() const;
Fp_model& invert();
Fp_model inverse() const;
Fp_model sqrt() const; // HAS TO BE A SQUARE (else does not terminate)
Fp_model operator^(const unsigned long pow) const;
template<mp_size_t m>
Fp_model operator^(const bigint<m> &pow) const;
static size_t size_in_bits() { return num_bits; }
static size_t capacity() { return num_bits - 1; }
static bigint<n> field_char() { return modulus; }
static Fp_model<n, modulus> zero();
static Fp_model<n, modulus> one();
static Fp_model<n, modulus> random_element();
friend std::ostream& operator<< <n,modulus>(std::ostream &out, const Fp_model<n, modulus> &p);
friend std::istream& operator>> <n,modulus>(std::istream &in, Fp_model<n, modulus> &p);
};
#ifdef PROFILE_OP_COUNTS
template<mp_size_t n, const bigint<n>& modulus>
long long Fp_model<n, modulus>::add_cnt = 0;
template<mp_size_t n, const bigint<n>& modulus>
long long Fp_model<n, modulus>::sub_cnt = 0;
template<mp_size_t n, const bigint<n>& modulus>
long long Fp_model<n, modulus>::mul_cnt = 0;
template<mp_size_t n, const bigint<n>& modulus>
long long Fp_model<n, modulus>::sqr_cnt = 0;
template<mp_size_t n, const bigint<n>& modulus>
long long Fp_model<n, modulus>::inv_cnt = 0;
#endif
template<mp_size_t n, const bigint<n>& modulus>
size_t Fp_model<n, modulus>::num_bits;
template<mp_size_t n, const bigint<n>& modulus>
bigint<n> Fp_model<n, modulus>::euler;
template<mp_size_t n, const bigint<n>& modulus>
size_t Fp_model<n, modulus>::s;
template<mp_size_t n, const bigint<n>& modulus>
bigint<n> Fp_model<n, modulus>::t;
template<mp_size_t n, const bigint<n>& modulus>
bigint<n> Fp_model<n, modulus>::t_minus_1_over_2;
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n, modulus> Fp_model<n, modulus>::nqr;
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n, modulus> Fp_model<n, modulus>::nqr_to_t;
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n, modulus> Fp_model<n, modulus>::multiplicative_generator;
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n, modulus> Fp_model<n, modulus>::root_of_unity;
template<mp_size_t n, const bigint<n>& modulus>
mp_limb_t Fp_model<n, modulus>::inv;
template<mp_size_t n, const bigint<n>& modulus>
bigint<n> Fp_model<n, modulus>::Rsquared;
template<mp_size_t n, const bigint<n>& modulus>
bigint<n> Fp_model<n, modulus>::Rcubed;
} // libsnark
#include "algebra/fields/fp.tcc"
#endif // FP_HPP_

View File

@@ -0,0 +1,790 @@
/** @file
*****************************************************************************
Implementation of arithmetic in the finite field F[p], for prime p of fixed length.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef FP_TCC_
#define FP_TCC_
#include <cassert>
#include <cstdlib>
#include <cmath>
#include "algebra/fields/fp_aux.tcc"
#include "algebra/fields/field_utils.hpp"
#include "common/assert_except.hpp"
namespace libsnark {
template<mp_size_t n, const bigint<n>& modulus>
void Fp_model<n,modulus>::mul_reduce(const bigint<n> &other)
{
/* stupid pre-processor tricks; beware */
#if defined(__x86_64__) && defined(USE_ASM)
if (n == 3)
{ // Use asm-optimized Comba multiplication and reduction
mp_limb_t res[2*n];
mp_limb_t c0, c1, c2;
COMBA_3_BY_3_MUL(c0, c1, c2, res, this->mont_repr.data, other.data);
mp_limb_t k;
mp_limb_t tmp1, tmp2, tmp3;
REDUCE_6_LIMB_PRODUCT(k, tmp1, tmp2, tmp3, inv, res, modulus.data);
/* subtract t > mod */
__asm__
("/* check for overflow */ \n\t"
MONT_CMP(16)
MONT_CMP(8)
MONT_CMP(0)
"/* subtract mod if overflow */ \n\t"
"subtract%=: \n\t"
MONT_FIRSTSUB
MONT_NEXTSUB(8)
MONT_NEXTSUB(16)
"done%=: \n\t"
:
: [tmp] "r" (res+n), [M] "r" (modulus.data)
: "cc", "memory", "%rax");
mpn_copyi(this->mont_repr.data, res+n, n);
}
else if (n == 4)
{ // use asm-optimized "CIOS method"
mp_limb_t tmp[n+1];
mp_limb_t T0=0, T1=1, cy=2, u=3; // TODO: fix this
__asm__ (MONT_PRECOMPUTE
MONT_FIRSTITER(1)
MONT_FIRSTITER(2)
MONT_FIRSTITER(3)
MONT_FINALIZE(3)
MONT_ITERFIRST(1)
MONT_ITERITER(1, 1)
MONT_ITERITER(1, 2)
MONT_ITERITER(1, 3)
MONT_FINALIZE(3)
MONT_ITERFIRST(2)
MONT_ITERITER(2, 1)
MONT_ITERITER(2, 2)
MONT_ITERITER(2, 3)
MONT_FINALIZE(3)
MONT_ITERFIRST(3)
MONT_ITERITER(3, 1)
MONT_ITERITER(3, 2)
MONT_ITERITER(3, 3)
MONT_FINALIZE(3)
"/* check for overflow */ \n\t"
MONT_CMP(24)
MONT_CMP(16)
MONT_CMP(8)
MONT_CMP(0)
"/* subtract mod if overflow */ \n\t"
"subtract%=: \n\t"
MONT_FIRSTSUB
MONT_NEXTSUB(8)
MONT_NEXTSUB(16)
MONT_NEXTSUB(24)
"done%=: \n\t"
:
: [tmp] "r" (tmp), [A] "r" (this->mont_repr.data), [B] "r" (other.data), [inv] "r" (inv), [M] "r" (modulus.data),
[T0] "r" (T0), [T1] "r" (T1), [cy] "r" (cy), [u] "r" (u)
: "cc", "memory", "%rax", "%rdx"
);
mpn_copyi(this->mont_repr.data, tmp, n);
}
else if (n == 5)
{ // use asm-optimized "CIOS method"
mp_limb_t tmp[n+1];
mp_limb_t T0=0, T1=1, cy=2, u=3; // TODO: fix this
__asm__ (MONT_PRECOMPUTE
MONT_FIRSTITER(1)
MONT_FIRSTITER(2)
MONT_FIRSTITER(3)
MONT_FIRSTITER(4)
MONT_FINALIZE(4)
MONT_ITERFIRST(1)
MONT_ITERITER(1, 1)
MONT_ITERITER(1, 2)
MONT_ITERITER(1, 3)
MONT_ITERITER(1, 4)
MONT_FINALIZE(4)
MONT_ITERFIRST(2)
MONT_ITERITER(2, 1)
MONT_ITERITER(2, 2)
MONT_ITERITER(2, 3)
MONT_ITERITER(2, 4)
MONT_FINALIZE(4)
MONT_ITERFIRST(3)
MONT_ITERITER(3, 1)
MONT_ITERITER(3, 2)
MONT_ITERITER(3, 3)
MONT_ITERITER(3, 4)
MONT_FINALIZE(4)
MONT_ITERFIRST(4)
MONT_ITERITER(4, 1)
MONT_ITERITER(4, 2)
MONT_ITERITER(4, 3)
MONT_ITERITER(4, 4)
MONT_FINALIZE(4)
"/* check for overflow */ \n\t"
MONT_CMP(32)
MONT_CMP(24)
MONT_CMP(16)
MONT_CMP(8)
MONT_CMP(0)
"/* subtract mod if overflow */ \n\t"
"subtract%=: \n\t"
MONT_FIRSTSUB
MONT_NEXTSUB(8)
MONT_NEXTSUB(16)
MONT_NEXTSUB(24)
MONT_NEXTSUB(32)
"done%=: \n\t"
:
: [tmp] "r" (tmp), [A] "r" (this->mont_repr.data), [B] "r" (other.data), [inv] "r" (inv), [M] "r" (modulus.data),
[T0] "r" (T0), [T1] "r" (T1), [cy] "r" (cy), [u] "r" (u)
: "cc", "memory", "%rax", "%rdx"
);
mpn_copyi(this->mont_repr.data, tmp, n);
}
else
#endif
{
mp_limb_t res[2*n];
mpn_mul_n(res, this->mont_repr.data, other.data, n);
/*
The Montgomery reduction here is based on Algorithm 14.32 in
Handbook of Applied Cryptography
<http://cacr.uwaterloo.ca/hac/about/chap14.pdf>.
*/
for (size_t i = 0; i < n; ++i)
{
mp_limb_t k = inv * res[i];
/* calculate res = res + k * mod * b^i */
mp_limb_t carryout = mpn_addmul_1(res+i, modulus.data, n, k);
carryout = mpn_add_1(res+n+i, res+n+i, n-i, carryout);
assert(carryout == 0);
}
if (mpn_cmp(res+n, modulus.data, n) >= 0)
{
const mp_limb_t borrow = mpn_sub(res+n, res+n, n, modulus.data, n);
assert(borrow == 0);
}
mpn_copyi(this->mont_repr.data, res+n, n);
}
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus>::Fp_model(const bigint<n> &b)
{
mpn_copyi(this->mont_repr.data, Rsquared.data, n);
mul_reduce(b);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus>::Fp_model(const long x, const bool is_unsigned)
{
if (is_unsigned || x >= 0)
{
this->mont_repr.data[0] = x;
}
else
{
const mp_limb_t borrow = mpn_sub_1(this->mont_repr.data, modulus.data, n, -x);
assert(borrow == 0);
}
mul_reduce(Rsquared);
}
template<mp_size_t n, const bigint<n>& modulus>
void Fp_model<n,modulus>::set_ulong(const unsigned long x)
{
this->mont_repr.clear();
this->mont_repr.data[0] = x;
mul_reduce(Rsquared);
}
template<mp_size_t n, const bigint<n>& modulus>
void Fp_model<n,modulus>::clear()
{
this->mont_repr.clear();
}
template<mp_size_t n, const bigint<n>& modulus>
bigint<n> Fp_model<n,modulus>::as_bigint() const
{
bigint<n> one;
one.clear();
one.data[0] = 1;
Fp_model<n, modulus> res(*this);
res.mul_reduce(one);
return (res.mont_repr);
}
template<mp_size_t n, const bigint<n>& modulus>
unsigned long Fp_model<n,modulus>::as_ulong() const
{
return this->as_bigint().as_ulong();
}
template<mp_size_t n, const bigint<n>& modulus>
bool Fp_model<n,modulus>::operator==(const Fp_model& other) const
{
return (this->mont_repr == other.mont_repr);
}
template<mp_size_t n, const bigint<n>& modulus>
bool Fp_model<n,modulus>::operator!=(const Fp_model& other) const
{
return (this->mont_repr != other.mont_repr);
}
template<mp_size_t n, const bigint<n>& modulus>
bool Fp_model<n,modulus>::is_zero() const
{
return (this->mont_repr.is_zero()); // zero maps to zero
}
template<mp_size_t n, const bigint<n>& modulus>
void Fp_model<n,modulus>::print() const
{
Fp_model<n,modulus> tmp;
tmp.mont_repr.data[0] = 1;
tmp.mul_reduce(this->mont_repr);
tmp.mont_repr.print();
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus> Fp_model<n,modulus>::zero()
{
Fp_model<n,modulus> res;
res.mont_repr.clear();
return res;
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus> Fp_model<n,modulus>::one()
{
Fp_model<n,modulus> res;
res.mont_repr.data[0] = 1;
res.mul_reduce(Rsquared);
return res;
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus>& Fp_model<n,modulus>::operator+=(const Fp_model<n,modulus>& other)
{
#ifdef PROFILE_OP_COUNTS
this->add_cnt++;
#endif
#if defined(__x86_64__) && defined(USE_ASM)
if (n == 3)
{
__asm__
("/* perform bignum addition */ \n\t"
ADD_FIRSTADD
ADD_NEXTADD(8)
ADD_NEXTADD(16)
"/* if overflow: subtract */ \n\t"
"/* (tricky point: if A and B are in the range we do not need to do anything special for the possible carry flag) */ \n\t"
"jc subtract%= \n\t"
"/* check for overflow */ \n\t"
ADD_CMP(16)
ADD_CMP(8)
ADD_CMP(0)
"/* subtract mod if overflow */ \n\t"
"subtract%=: \n\t"
ADD_FIRSTSUB
ADD_NEXTSUB(8)
ADD_NEXTSUB(16)
"done%=: \n\t"
:
: [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data)
: "cc", "memory", "%rax");
}
else if (n == 4)
{
__asm__
("/* perform bignum addition */ \n\t"
ADD_FIRSTADD
ADD_NEXTADD(8)
ADD_NEXTADD(16)
ADD_NEXTADD(24)
"/* if overflow: subtract */ \n\t"
"/* (tricky point: if A and B are in the range we do not need to do anything special for the possible carry flag) */ \n\t"
"jc subtract%= \n\t"
"/* check for overflow */ \n\t"
ADD_CMP(24)
ADD_CMP(16)
ADD_CMP(8)
ADD_CMP(0)
"/* subtract mod if overflow */ \n\t"
"subtract%=: \n\t"
ADD_FIRSTSUB
ADD_NEXTSUB(8)
ADD_NEXTSUB(16)
ADD_NEXTSUB(24)
"done%=: \n\t"
:
: [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data)
: "cc", "memory", "%rax");
}
else if (n == 5)
{
__asm__
("/* perform bignum addition */ \n\t"
ADD_FIRSTADD
ADD_NEXTADD(8)
ADD_NEXTADD(16)
ADD_NEXTADD(24)
ADD_NEXTADD(32)
"/* if overflow: subtract */ \n\t"
"/* (tricky point: if A and B are in the range we do not need to do anything special for the possible carry flag) */ \n\t"
"jc subtract%= \n\t"
"/* check for overflow */ \n\t"
ADD_CMP(32)
ADD_CMP(24)
ADD_CMP(16)
ADD_CMP(8)
ADD_CMP(0)
"/* subtract mod if overflow */ \n\t"
"subtract%=: \n\t"
ADD_FIRSTSUB
ADD_NEXTSUB(8)
ADD_NEXTSUB(16)
ADD_NEXTSUB(24)
ADD_NEXTSUB(32)
"done%=: \n\t"
:
: [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data)
: "cc", "memory", "%rax");
}
else
#endif
{
mp_limb_t scratch[n+1];
const mp_limb_t carry = mpn_add_n(scratch, this->mont_repr.data, other.mont_repr.data, n);
scratch[n] = carry;
if (carry || mpn_cmp(scratch, modulus.data, n) >= 0)
{
const mp_limb_t borrow = mpn_sub(scratch, scratch, n+1, modulus.data, n);
assert(borrow == 0);
}
mpn_copyi(this->mont_repr.data, scratch, n);
}
return *this;
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus>& Fp_model<n,modulus>::operator-=(const Fp_model<n,modulus>& other)
{
#ifdef PROFILE_OP_COUNTS
this->sub_cnt++;
#endif
#if defined(__x86_64__) && defined(USE_ASM)
if (n == 3)
{
__asm__
(SUB_FIRSTSUB
SUB_NEXTSUB(8)
SUB_NEXTSUB(16)
"jnc done%=\n\t"
SUB_FIRSTADD
SUB_NEXTADD(8)
SUB_NEXTADD(16)
"done%=:\n\t"
:
: [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data)
: "cc", "memory", "%rax");
}
else if (n == 4)
{
__asm__
(SUB_FIRSTSUB
SUB_NEXTSUB(8)
SUB_NEXTSUB(16)
SUB_NEXTSUB(24)
"jnc done%=\n\t"
SUB_FIRSTADD
SUB_NEXTADD(8)
SUB_NEXTADD(16)
SUB_NEXTADD(24)
"done%=:\n\t"
:
: [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data)
: "cc", "memory", "%rax");
}
else if (n == 5)
{
__asm__
(SUB_FIRSTSUB
SUB_NEXTSUB(8)
SUB_NEXTSUB(16)
SUB_NEXTSUB(24)
SUB_NEXTSUB(32)
"jnc done%=\n\t"
SUB_FIRSTADD
SUB_NEXTADD(8)
SUB_NEXTADD(16)
SUB_NEXTADD(24)
SUB_NEXTADD(32)
"done%=:\n\t"
:
: [A] "r" (this->mont_repr.data), [B] "r" (other.mont_repr.data), [mod] "r" (modulus.data)
: "cc", "memory", "%rax");
}
else
#endif
{
mp_limb_t scratch[n+1];
if (mpn_cmp(this->mont_repr.data, other.mont_repr.data, n) < 0)
{
const mp_limb_t carry = mpn_add_n(scratch, this->mont_repr.data, modulus.data, n);
scratch[n] = carry;
}
else
{
mpn_copyi(scratch, this->mont_repr.data, n);
scratch[n] = 0;
}
const mp_limb_t borrow = mpn_sub(scratch, scratch, n+1, other.mont_repr.data, n);
assert(borrow == 0);
mpn_copyi(this->mont_repr.data, scratch, n);
}
return *this;
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus>& Fp_model<n,modulus>::operator*=(const Fp_model<n,modulus>& other)
{
#ifdef PROFILE_OP_COUNTS
this->mul_cnt++;
#endif
mul_reduce(other.mont_repr);
return *this;
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus>& Fp_model<n,modulus>::operator^=(const unsigned long pow)
{
(*this) = power<Fp_model<n, modulus> >(*this, pow);
return (*this);
}
template<mp_size_t n, const bigint<n>& modulus>
template<mp_size_t m>
Fp_model<n,modulus>& Fp_model<n,modulus>::operator^=(const bigint<m> &pow)
{
(*this) = power<Fp_model<n, modulus>, m>(*this, pow);
return (*this);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus> Fp_model<n,modulus>::operator+(const Fp_model<n,modulus>& other) const
{
Fp_model<n, modulus> r(*this);
return (r += other);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus> Fp_model<n,modulus>::operator-(const Fp_model<n,modulus>& other) const
{
Fp_model<n, modulus> r(*this);
return (r -= other);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus> Fp_model<n,modulus>::operator*(const Fp_model<n,modulus>& other) const
{
Fp_model<n, modulus> r(*this);
return (r *= other);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus> Fp_model<n,modulus>::operator^(const unsigned long pow) const
{
Fp_model<n, modulus> r(*this);
return (r ^= pow);
}
template<mp_size_t n, const bigint<n>& modulus>
template<mp_size_t m>
Fp_model<n,modulus> Fp_model<n,modulus>::operator^(const bigint<m> &pow) const
{
Fp_model<n, modulus> r(*this);
return (r ^= pow);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus> Fp_model<n,modulus>::operator-() const
{
#ifdef PROFILE_OP_COUNTS
this->sub_cnt++;
#endif
if (this->is_zero())
{
return (*this);
}
else
{
Fp_model<n, modulus> r;
mpn_sub_n(r.mont_repr.data, modulus.data, this->mont_repr.data, n);
return r;
}
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus> Fp_model<n,modulus>::squared() const
{
#ifdef PROFILE_OP_COUNTS
this->sqr_cnt++;
this->mul_cnt--; // zero out the upcoming mul
#endif
/* stupid pre-processor tricks; beware */
#if defined(__x86_64__) && defined(USE_ASM)
if (n == 3)
{ // use asm-optimized Comba squaring
mp_limb_t res[2*n];
mp_limb_t c0, c1, c2;
COMBA_3_BY_3_SQR(c0, c1, c2, res, this->mont_repr.data);
mp_limb_t k;
mp_limb_t tmp1, tmp2, tmp3;
REDUCE_6_LIMB_PRODUCT(k, tmp1, tmp2, tmp3, inv, res, modulus.data);
/* subtract t > mod */
__asm__ volatile
("/* check for overflow */ \n\t"
MONT_CMP(16)
MONT_CMP(8)
MONT_CMP(0)
"/* subtract mod if overflow */ \n\t"
"subtract%=: \n\t"
MONT_FIRSTSUB
MONT_NEXTSUB(8)
MONT_NEXTSUB(16)
"done%=: \n\t"
:
: [tmp] "r" (res+n), [M] "r" (modulus.data)
: "cc", "memory", "%rax");
Fp_model<n, modulus> r;
mpn_copyi(r.mont_repr.data, res+n, n);
return r;
}
else
#endif
{
Fp_model<n, modulus> r(*this);
return (r *= r);
}
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus>& Fp_model<n,modulus>::invert()
{
#ifdef PROFILE_OP_COUNTS
this->inv_cnt++;
#endif
assert(!this->is_zero());
bigint<n> g; /* gp should have room for vn = n limbs */
mp_limb_t s[n+1]; /* sp should have room for vn+1 limbs */
mp_size_t sn;
bigint<n> v = modulus; // both source operands are destroyed by mpn_gcdext
/* computes gcd(u, v) = g = u*s + v*t, so s*u will be 1 (mod v) */
const mp_size_t gn = mpn_gcdext(g.data, s, &sn, this->mont_repr.data, n, v.data, n);
assert(gn == 1 && g.data[0] == 1); /* inverse exists */
mp_limb_t q; /* division result fits into q, as sn <= n+1 */
/* sn < 0 indicates negative sn; will fix up later */
if (std::abs(sn) >= n)
{
/* if sn could require modulus reduction, do it here */
mpn_tdiv_qr(&q, this->mont_repr.data, 0, s, std::abs(sn), modulus.data, n);
}
else
{
/* otherwise just copy it over */
mpn_zero(this->mont_repr.data, n);
mpn_copyi(this->mont_repr.data, s, std::abs(sn));
}
/* fix up the negative sn */
if (sn < 0)
{
const mp_limb_t borrow = mpn_sub_n(this->mont_repr.data, modulus.data, this->mont_repr.data, n);
assert(borrow == 0);
}
mul_reduce(Rcubed);
return *this;
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus> Fp_model<n,modulus>::inverse() const
{
Fp_model<n, modulus> r(*this);
return (r.invert());
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n, modulus> Fp_model<n,modulus>::random_element() /// returns random element of Fp_model
{
/* note that as Montgomery representation is a bijection then
selecting a random element of {xR} is the same as selecting a
random element of {x} */
Fp_model<n, modulus> r;
do
{
r.mont_repr.randomize();
/* clear all bits higher than MSB of modulus */
size_t bitno = GMP_NUMB_BITS * n - 1;
while (modulus.test_bit(bitno) == false)
{
const std::size_t part = bitno/GMP_NUMB_BITS;
const std::size_t bit = bitno - (GMP_NUMB_BITS*part);
r.mont_repr.data[part] &= ~(1ul<<bit);
bitno--;
}
}
/* if r.data is still >= modulus -- repeat (rejection sampling) */
while (mpn_cmp(r.mont_repr.data, modulus.data, n) >= 0);
return r;
}
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n,modulus> Fp_model<n,modulus>::sqrt() const
{
if (is_zero()) {
return *this;
}
Fp_model<n,modulus> one = Fp_model<n,modulus>::one();
size_t v = Fp_model<n,modulus>::s;
Fp_model<n,modulus> z = Fp_model<n,modulus>::nqr_to_t;
Fp_model<n,modulus> w = (*this)^Fp_model<n,modulus>::t_minus_1_over_2;
Fp_model<n,modulus> x = (*this) * w;
Fp_model<n,modulus> b = x * w; // b = (*this)^t
// check if square with euler's criterion
Fp_model<n,modulus> check = b;
for (size_t i = 0; i < v-1; ++i)
{
check = check.squared();
}
if (check != one)
{
assert_except(0);
}
// compute square root with Tonelli--Shanks
// (does not terminate if not a square!)
while (b != one)
{
size_t m = 0;
Fp_model<n,modulus> b2m = b;
while (b2m != one)
{
/* invariant: b2m = b^(2^m) after entering this loop */
b2m = b2m.squared();
m += 1;
}
int j = v-m-1;
w = z;
while (j > 0)
{
w = w.squared();
--j;
} // w = z^2^(v-m-1)
z = w.squared();
b = b * z;
x = x * w;
v = m;
}
return x;
}
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream &out, const Fp_model<n, modulus> &p)
{
#ifndef MONTGOMERY_OUTPUT
Fp_model<n,modulus> tmp;
tmp.mont_repr.data[0] = 1;
tmp.mul_reduce(p.mont_repr);
out << tmp.mont_repr;
#else
out << p.mont_repr;
#endif
return out;
}
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream &in, Fp_model<n, modulus> &p)
{
#ifndef MONTGOMERY_OUTPUT
in >> p.mont_repr;
p.mul_reduce(Fp_model<n, modulus>::Rsquared);
#else
in >> p.mont_repr;
#endif
return in;
}
} // libsnark
#endif // FP_TCC_

View File

@@ -0,0 +1,116 @@
/** @file
*****************************************************************************
Declaration of arithmetic in the finite field F[((p^2)^3)^2].
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef FP12_2OVER3OVER2_HPP_
#define FP12_2OVER3OVER2_HPP_
#include "algebra/fields/fp.hpp"
#include "algebra/fields/fp2.hpp"
#include "algebra/fields/fp6_3over2.hpp"
#include <vector>
namespace libsnark {
template<mp_size_t n, const bigint<n>& modulus>
class Fp12_2over3over2_model;
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream &, const Fp12_2over3over2_model<n, modulus> &);
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream &, Fp12_2over3over2_model<n, modulus> &);
/**
* Arithmetic in the finite field F[((p^2)^3)^2].
*
* Let p := modulus. This interface provides arithmetic for the extension field
* Fp12 = Fp6[W]/(W^2-V) where Fp6 = Fp2[V]/(V^3-non_residue) and non_residue is in Fp2
*
* ASSUMPTION: p = 1 (mod 6)
*/
template<mp_size_t n, const bigint<n>& modulus>
class Fp12_2over3over2_model {
public:
typedef Fp_model<n, modulus> my_Fp;
typedef Fp2_model<n, modulus> my_Fp2;
typedef Fp6_3over2_model<n, modulus> my_Fp6;
static Fp2_model<n, modulus> non_residue;
static Fp2_model<n, modulus> Frobenius_coeffs_c1[12]; // non_residue^((modulus^i-1)/6) for i=0,...,11
my_Fp6 c0, c1;
Fp12_2over3over2_model() {};
Fp12_2over3over2_model(const my_Fp6& c0, const my_Fp6& c1) : c0(c0), c1(c1) {};
void clear() { c0.clear(); c1.clear(); }
void print() const { printf("c0/c1:\n"); c0.print(); c1.print(); }
static Fp12_2over3over2_model<n, modulus> zero();
static Fp12_2over3over2_model<n, modulus> one();
static Fp12_2over3over2_model<n, modulus> random_element();
bool is_zero() const { return c0.is_zero() && c1.is_zero(); }
bool operator==(const Fp12_2over3over2_model &other) const;
bool operator!=(const Fp12_2over3over2_model &other) const;
Fp12_2over3over2_model operator+(const Fp12_2over3over2_model &other) const;
Fp12_2over3over2_model operator-(const Fp12_2over3over2_model &other) const;
Fp12_2over3over2_model operator*(const Fp12_2over3over2_model &other) const;
Fp12_2over3over2_model operator-() const;
Fp12_2over3over2_model squared() const; // default is squared_complex
Fp12_2over3over2_model squared_karatsuba() const;
Fp12_2over3over2_model squared_complex() const;
Fp12_2over3over2_model inverse() const;
Fp12_2over3over2_model Frobenius_map(unsigned long power) const;
Fp12_2over3over2_model unitary_inverse() const;
Fp12_2over3over2_model cyclotomic_squared() const;
Fp12_2over3over2_model mul_by_024(const my_Fp2 &ell_0, const my_Fp2 &ell_VW, const my_Fp2 &ell_VV) const;
static my_Fp6 mul_by_non_residue(const my_Fp6 &elt);
template<mp_size_t m>
Fp12_2over3over2_model cyclotomic_exp(const bigint<m> &exponent) const;
static bigint<n> base_field_char() { return modulus; }
static size_t extension_degree() { return 12; }
friend std::ostream& operator<< <n, modulus>(std::ostream &out, const Fp12_2over3over2_model<n, modulus> &el);
friend std::istream& operator>> <n, modulus>(std::istream &in, Fp12_2over3over2_model<n, modulus> &el);
};
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream& out, const std::vector<Fp12_2over3over2_model<n, modulus> > &v);
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream& in, std::vector<Fp12_2over3over2_model<n, modulus> > &v);
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp12_2over3over2_model<n, modulus> &rhs);
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n, modulus> operator*(const Fp2_model<n, modulus> &lhs, const Fp12_2over3over2_model<n, modulus> &rhs);
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n, modulus> operator*(const Fp6_3over2_model<n, modulus> &lhs, const Fp12_2over3over2_model<n, modulus> &rhs);
template<mp_size_t n, const bigint<n>& modulus, mp_size_t m>
Fp12_2over3over2_model<n, modulus> operator^(const Fp12_2over3over2_model<n, modulus> &self, const bigint<m> &exponent);
template<mp_size_t n, const bigint<n>& modulus, mp_size_t m, const bigint<m>& exp_modulus>
Fp12_2over3over2_model<n, modulus> operator^(const Fp12_2over3over2_model<n, modulus> &self, const Fp_model<m, exp_modulus> &exponent);
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n, modulus> Fp12_2over3over2_model<n, modulus>::non_residue;
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n, modulus> Fp12_2over3over2_model<n, modulus>::Frobenius_coeffs_c1[12];
} // libsnark
#include "algebra/fields/fp12_2over3over2.tcc"
#endif // FP12_2OVER3OVER2_HPP_

View File

@@ -0,0 +1,412 @@
/** @file
*****************************************************************************
Implementation of arithmetic in the finite field F[((p^2)^3)^2].
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef FP12_2OVER3OVER2_TCC_
#define FP12_2OVER3OVER2_TCC_
namespace libsnark {
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n, modulus> Fp12_2over3over2_model<n,modulus>::mul_by_non_residue(const Fp6_3over2_model<n, modulus> &elt)
{
return Fp6_3over2_model<n, modulus>(non_residue * elt.c2, elt.c0, elt.c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::zero()
{
return Fp12_2over3over2_model<n, modulus>(my_Fp6::zero(), my_Fp6::zero());
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::one()
{
return Fp12_2over3over2_model<n, modulus>(my_Fp6::one(), my_Fp6::zero());
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::random_element()
{
Fp12_2over3over2_model<n, modulus> r;
r.c0 = my_Fp6::random_element();
r.c1 = my_Fp6::random_element();
return r;
}
template<mp_size_t n, const bigint<n>& modulus>
bool Fp12_2over3over2_model<n,modulus>::operator==(const Fp12_2over3over2_model<n,modulus> &other) const
{
return (this->c0 == other.c0 && this->c1 == other.c1);
}
template<mp_size_t n, const bigint<n>& modulus>
bool Fp12_2over3over2_model<n,modulus>::operator!=(const Fp12_2over3over2_model<n,modulus> &other) const
{
return !(operator==(other));
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::operator+(const Fp12_2over3over2_model<n,modulus> &other) const
{
return Fp12_2over3over2_model<n,modulus>(this->c0 + other.c0,
this->c1 + other.c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::operator-(const Fp12_2over3over2_model<n,modulus> &other) const
{
return Fp12_2over3over2_model<n,modulus>(this->c0 - other.c0,
this->c1 - other.c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp12_2over3over2_model<n, modulus> &rhs)
{
return Fp12_2over3over2_model<n,modulus>(lhs*rhs.c0,
lhs*rhs.c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n, modulus> operator*(const Fp2_model<n, modulus> &lhs, const Fp12_2over3over2_model<n, modulus> &rhs)
{
return Fp12_2over3over2_model<n,modulus>(lhs*rhs.c0,
lhs*rhs.c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n, modulus> operator*(const Fp6_3over2_model<n, modulus> &lhs, const Fp12_2over3over2_model<n, modulus> &rhs)
{
return Fp12_2over3over2_model<n,modulus>(lhs*rhs.c0,
lhs*rhs.c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::operator*(const Fp12_2over3over2_model<n,modulus> &other) const
{
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Karatsuba) */
const my_Fp6 &A = other.c0, &B = other.c1,
&a = this->c0, &b = this->c1;
const my_Fp6 aA = a * A;
const my_Fp6 bB = b * B;
return Fp12_2over3over2_model<n,modulus>(aA + Fp12_2over3over2_model<n, modulus>::mul_by_non_residue(bB),
(a + b)*(A+B) - aA - bB);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::operator-() const
{
return Fp12_2over3over2_model<n,modulus>(-this->c0,
-this->c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::squared() const
{
return squared_complex();
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::squared_karatsuba() const
{
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Karatsuba squaring) */
const my_Fp6 &a = this->c0, &b = this->c1;
const my_Fp6 asq = a.squared();
const my_Fp6 bsq = b.squared();
return Fp12_2over3over2_model<n,modulus>(asq + Fp12_2over3over2_model<n, modulus>::mul_by_non_residue(bsq),
(a + b).squared() - asq - bsq);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::squared_complex() const
{
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Complex squaring) */
const my_Fp6 &a = this->c0, &b = this->c1;
const my_Fp6 ab = a * b;
return Fp12_2over3over2_model<n,modulus>((a + b) * (a + Fp12_2over3over2_model<n, modulus>::mul_by_non_residue(b)) - ab - Fp12_2over3over2_model<n, modulus>::mul_by_non_residue(ab),
ab + ab);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::inverse() const
{
/* From "High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves"; Algorithm 8 */
const my_Fp6 &a = this->c0, &b = this->c1;
const my_Fp6 t0 = a.squared();
const my_Fp6 t1 = b.squared();
const my_Fp6 t2 = t0 - Fp12_2over3over2_model<n, modulus>::mul_by_non_residue(t1);
const my_Fp6 t3 = t2.inverse();
const my_Fp6 c0 = a * t3;
const my_Fp6 c1 = - (b * t3);
return Fp12_2over3over2_model<n,modulus>(c0, c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::Frobenius_map(unsigned long power) const
{
return Fp12_2over3over2_model<n,modulus>(c0.Frobenius_map(power),
Frobenius_coeffs_c1[power % 12] * c1.Frobenius_map(power));
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::unitary_inverse() const
{
return Fp12_2over3over2_model<n,modulus>(this->c0,
-this->c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::cyclotomic_squared() const
{
/* OLD: naive implementation
return (*this).squared();
*/
my_Fp2 z0 = this->c0.c0;
my_Fp2 z4 = this->c0.c1;
my_Fp2 z3 = this->c0.c2;
my_Fp2 z2 = this->c1.c0;
my_Fp2 z1 = this->c1.c1;
my_Fp2 z5 = this->c1.c2;
my_Fp2 t0, t1, t2, t3, t4, t5, tmp;
// t0 + t1*y = (z0 + z1*y)^2 = a^2
tmp = z0 * z1;
t0 = (z0 + z1) * (z0 + my_Fp6::non_residue * z1) - tmp - my_Fp6::non_residue * tmp;
t1 = tmp + tmp;
// t2 + t3*y = (z2 + z3*y)^2 = b^2
tmp = z2 * z3;
t2 = (z2 + z3) * (z2 + my_Fp6::non_residue * z3) - tmp - my_Fp6::non_residue * tmp;
t3 = tmp + tmp;
// t4 + t5*y = (z4 + z5*y)^2 = c^2
tmp = z4 * z5;
t4 = (z4 + z5) * (z4 + my_Fp6::non_residue * z5) - tmp - my_Fp6::non_residue * tmp;
t5 = tmp + tmp;
// for A
// z0 = 3 * t0 - 2 * z0
z0 = t0 - z0;
z0 = z0 + z0;
z0 = z0 + t0;
// z1 = 3 * t1 + 2 * z1
z1 = t1 + z1;
z1 = z1 + z1;
z1 = z1 + t1;
// for B
// z2 = 3 * (xi * t5) + 2 * z2
tmp = my_Fp6::non_residue * t5;
z2 = tmp + z2;
z2 = z2 + z2;
z2 = z2 + tmp;
// z3 = 3 * t4 - 2 * z3
z3 = t4 - z3;
z3 = z3 + z3;
z3 = z3 + t4;
// for C
// z4 = 3 * t2 - 2 * z4
z4 = t2 - z4;
z4 = z4 + z4;
z4 = z4 + t2;
// z5 = 3 * t3 + 2 * z5
z5 = t3 + z5;
z5 = z5 + z5;
z5 = z5 + t3;
return Fp12_2over3over2_model<n,modulus>(my_Fp6(z0,z4,z3),my_Fp6(z2,z1,z5));
}
template<mp_size_t n, const bigint<n>& modulus>
Fp12_2over3over2_model<n,modulus> Fp12_2over3over2_model<n,modulus>::mul_by_024(const Fp2_model<n, modulus> &ell_0,
const Fp2_model<n, modulus> &ell_VW,
const Fp2_model<n, modulus> &ell_VV) const
{
/* OLD: naive implementation
Fp12_2over3over2_model<n,modulus> a(my_Fp6(ell_0, my_Fp2::zero(), ell_VV),
my_Fp6(my_Fp2::zero(), ell_VW, my_Fp2::zero()));
return (*this) * a;
*/
my_Fp2 z0 = this->c0.c0;
my_Fp2 z1 = this->c0.c1;
my_Fp2 z2 = this->c0.c2;
my_Fp2 z3 = this->c1.c0;
my_Fp2 z4 = this->c1.c1;
my_Fp2 z5 = this->c1.c2;
my_Fp2 x0 = ell_0;
my_Fp2 x2 = ell_VV;
my_Fp2 x4 = ell_VW;
my_Fp2 t0, t1, t2, s0, T3, T4, D0, D2, D4, S1;
D0 = z0 * x0;
D2 = z2 * x2;
D4 = z4 * x4;
t2 = z0 + z4;
t1 = z0 + z2;
s0 = z1 + z3 + z5;
// For z.a_.a_ = z0.
S1 = z1 * x2;
T3 = S1 + D4;
T4 = my_Fp6::non_residue * T3 + D0;
z0 = T4;
// For z.a_.b_ = z1
T3 = z5 * x4;
S1 = S1 + T3;
T3 = T3 + D2;
T4 = my_Fp6::non_residue * T3;
T3 = z1 * x0;
S1 = S1 + T3;
T4 = T4 + T3;
z1 = T4;
// For z.a_.c_ = z2
t0 = x0 + x2;
T3 = t1 * t0 - D0 - D2;
T4 = z3 * x4;
S1 = S1 + T4;
T3 = T3 + T4;
// For z.b_.a_ = z3 (z3 needs z2)
t0 = z2 + z4;
z2 = T3;
t1 = x2 + x4;
T3 = t0 * t1 - D2 - D4;
T4 = my_Fp6::non_residue * T3;
T3 = z3 * x0;
S1 = S1 + T3;
T4 = T4 + T3;
z3 = T4;
// For z.b_.b_ = z4
T3 = z5 * x2;
S1 = S1 + T3;
T4 = my_Fp6::non_residue * T3;
t0 = x0 + x4;
T3 = t2 * t0 - D0 - D4;
T4 = T4 + T3;
z4 = T4;
// For z.b_.c_ = z5.
t0 = x0 + x2 + x4;
T3 = s0 * t0 - S1;
z5 = T3;
return Fp12_2over3over2_model<n,modulus>(my_Fp6(z0,z1,z2),my_Fp6(z3,z4,z5));
}
template<mp_size_t n, const bigint<n>& modulus, mp_size_t m>
Fp12_2over3over2_model<n, modulus> operator^(const Fp12_2over3over2_model<n, modulus> &self, const bigint<m> &exponent)
{
return power<Fp12_2over3over2_model<n, modulus> >(self, exponent);
}
template<mp_size_t n, const bigint<n>& modulus, mp_size_t m, const bigint<m>& exp_modulus>
Fp12_2over3over2_model<n, modulus> operator^(const Fp12_2over3over2_model<n, modulus> &self, const Fp_model<m, exp_modulus> &exponent)
{
return self^(exponent.as_bigint());
}
template<mp_size_t n, const bigint<n>& modulus>
template<mp_size_t m>
Fp12_2over3over2_model<n, modulus> Fp12_2over3over2_model<n,modulus>::cyclotomic_exp(const bigint<m> &exponent) const
{
Fp12_2over3over2_model<n,modulus> res = Fp12_2over3over2_model<n,modulus>::one();
bool found_one = false;
for (long i = m-1; i >= 0; --i)
{
for (long j = GMP_NUMB_BITS - 1; j >= 0; --j)
{
if (found_one)
{
res = res.cyclotomic_squared();
}
if (exponent.data[i] & (1ul<<j))
{
found_one = true;
res = res * (*this);
}
}
}
return res;
}
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream &out, const Fp12_2over3over2_model<n, modulus> &el)
{
out << el.c0 << OUTPUT_SEPARATOR << el.c1;
return out;
}
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream &in, Fp12_2over3over2_model<n, modulus> &el)
{
in >> el.c0 >> el.c1;
return in;
}
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream& out, const std::vector<Fp12_2over3over2_model<n, modulus> > &v)
{
out << v.size() << "\n";
for (const Fp12_2over3over2_model<n, modulus>& t : v)
{
out << t << OUTPUT_NEWLINE;
}
return out;
}
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream& in, std::vector<Fp12_2over3over2_model<n, modulus> > &v)
{
v.clear();
size_t s;
in >> s;
char b;
in.read(&b, 1);
v.reserve(s);
for (size_t i = 0; i < s; ++i)
{
Fp12_2over3over2_model<n, modulus> el;
in >> el;
v.emplace_back(el);
}
return in;
}
} // libsnark
#endif // FP12_2OVER3OVER2_TCC_

View File

@@ -0,0 +1,120 @@
/** @file
*****************************************************************************
Implementation of arithmetic in the finite field F[p^2].
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef FP2_HPP_
#define FP2_HPP_
#include "algebra/fields/fp.hpp"
#include <vector>
namespace libsnark {
template<mp_size_t n, const bigint<n>& modulus>
class Fp2_model;
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream &, const Fp2_model<n, modulus> &);
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream &, Fp2_model<n, modulus> &);
/**
* Arithmetic in the field F[p^3].
*
* Let p := modulus. This interface provides arithmetic for the extension field
* Fp2 = Fp[U]/(U^2-non_residue), where non_residue is in Fp.
*
* ASSUMPTION: p = 1 (mod 6)
*/
template<mp_size_t n, const bigint<n>& modulus>
class Fp2_model {
public:
typedef Fp_model<n, modulus> my_Fp;
static bigint<2*n> euler; // (modulus^2-1)/2
static size_t s; // modulus^2 = 2^s * t + 1
static bigint<2*n> t; // with t odd
static bigint<2*n> t_minus_1_over_2; // (t-1)/2
static my_Fp non_residue; // X^4-non_residue irreducible over Fp; used for constructing Fp2 = Fp[X] / (X^2 - non_residue)
static Fp2_model<n, modulus> nqr; // a quadratic nonresidue in Fp2
static Fp2_model<n, modulus> nqr_to_t; // nqr^t
static my_Fp Frobenius_coeffs_c1[2]; // non_residue^((modulus^i-1)/2) for i=0,1
my_Fp c0, c1;
Fp2_model() {};
Fp2_model(const my_Fp& c0, const my_Fp& c1) : c0(c0), c1(c1) {};
void clear() { c0.clear(); c1.clear(); }
void print() const { printf("c0/c1:\n"); c0.print(); c1.print(); }
static Fp2_model<n, modulus> zero();
static Fp2_model<n, modulus> one();
static Fp2_model<n, modulus> random_element();
bool is_zero() const { return c0.is_zero() && c1.is_zero(); }
bool operator==(const Fp2_model &other) const;
bool operator!=(const Fp2_model &other) const;
Fp2_model operator+(const Fp2_model &other) const;
Fp2_model operator-(const Fp2_model &other) const;
Fp2_model operator*(const Fp2_model &other) const;
Fp2_model operator-() const;
Fp2_model squared() const; // default is squared_complex
Fp2_model inverse() const;
Fp2_model Frobenius_map(unsigned long power) const;
Fp2_model sqrt() const; // HAS TO BE A SQUARE (else does not terminate)
Fp2_model squared_karatsuba() const;
Fp2_model squared_complex() const;
template<mp_size_t m>
Fp2_model operator^(const bigint<m> &other) const;
static size_t size_in_bits() { return 2*my_Fp::size_in_bits(); }
static bigint<n> base_field_char() { return modulus; }
friend std::ostream& operator<< <n, modulus>(std::ostream &out, const Fp2_model<n, modulus> &el);
friend std::istream& operator>> <n, modulus>(std::istream &in, Fp2_model<n, modulus> &el);
};
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream& out, const std::vector<Fp2_model<n, modulus> > &v);
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream& in, std::vector<Fp2_model<n, modulus> > &v);
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp2_model<n, modulus> &rhs);
template<mp_size_t n, const bigint<n>& modulus>
bigint<2*n> Fp2_model<n, modulus>::euler;
template<mp_size_t n, const bigint<n>& modulus>
size_t Fp2_model<n, modulus>::s;
template<mp_size_t n, const bigint<n>& modulus>
bigint<2*n> Fp2_model<n, modulus>::t;
template<mp_size_t n, const bigint<n>& modulus>
bigint<2*n> Fp2_model<n, modulus>::t_minus_1_over_2;
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n, modulus> Fp2_model<n, modulus>::non_residue;
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n, modulus> Fp2_model<n, modulus>::nqr;
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n, modulus> Fp2_model<n, modulus>::nqr_to_t;
template<mp_size_t n, const bigint<n>& modulus>
Fp_model<n, modulus> Fp2_model<n, modulus>::Frobenius_coeffs_c1[2];
} // libsnark
#include "algebra/fields/fp2.tcc"
#endif // FP2_HPP_

View File

@@ -0,0 +1,261 @@
/** @file
*****************************************************************************
Implementation of arithmetic in the finite field F[p^2].
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef FP2_TCC_
#define FP2_TCC_
#include "algebra/fields/field_utils.hpp"
namespace libsnark {
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n,modulus> Fp2_model<n,modulus>::zero()
{
return Fp2_model<n, modulus>(my_Fp::zero(), my_Fp::zero());
}
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n,modulus> Fp2_model<n,modulus>::one()
{
return Fp2_model<n, modulus>(my_Fp::one(), my_Fp::zero());
}
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n,modulus> Fp2_model<n,modulus>::random_element()
{
Fp2_model<n, modulus> r;
r.c0 = my_Fp::random_element();
r.c1 = my_Fp::random_element();
return r;
}
template<mp_size_t n, const bigint<n>& modulus>
bool Fp2_model<n,modulus>::operator==(const Fp2_model<n,modulus> &other) const
{
return (this->c0 == other.c0 && this->c1 == other.c1);
}
template<mp_size_t n, const bigint<n>& modulus>
bool Fp2_model<n,modulus>::operator!=(const Fp2_model<n,modulus> &other) const
{
return !(operator==(other));
}
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n,modulus> Fp2_model<n,modulus>::operator+(const Fp2_model<n,modulus> &other) const
{
return Fp2_model<n,modulus>(this->c0 + other.c0,
this->c1 + other.c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n,modulus> Fp2_model<n,modulus>::operator-(const Fp2_model<n,modulus> &other) const
{
return Fp2_model<n,modulus>(this->c0 - other.c0,
this->c1 - other.c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp2_model<n, modulus> &rhs)
{
return Fp2_model<n,modulus>(lhs*rhs.c0,
lhs*rhs.c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n,modulus> Fp2_model<n,modulus>::operator*(const Fp2_model<n,modulus> &other) const
{
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Karatsuba) */
const my_Fp
&A = other.c0, &B = other.c1,
&a = this->c0, &b = this->c1;
const my_Fp aA = a * A;
const my_Fp bB = b * B;
return Fp2_model<n,modulus>(aA + non_residue * bB,
(a + b)*(A+B) - aA - bB);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n,modulus> Fp2_model<n,modulus>::operator-() const
{
return Fp2_model<n,modulus>(-this->c0,
-this->c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n,modulus> Fp2_model<n,modulus>::squared() const
{
return squared_complex();
}
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n,modulus> Fp2_model<n,modulus>::squared_karatsuba() const
{
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Karatsuba squaring) */
const my_Fp &a = this->c0, &b = this->c1;
const my_Fp asq = a.squared();
const my_Fp bsq = b.squared();
return Fp2_model<n,modulus>(asq + non_residue * bsq,
(a + b).squared() - asq - bsq);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n,modulus> Fp2_model<n,modulus>::squared_complex() const
{
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 3 (Complex squaring) */
const my_Fp &a = this->c0, &b = this->c1;
const my_Fp ab = a * b;
return Fp2_model<n,modulus>((a + b) * (a + non_residue * b) - ab - non_residue * ab,
ab + ab);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n,modulus> Fp2_model<n,modulus>::inverse() const
{
const my_Fp &a = this->c0, &b = this->c1;
/* From "High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves"; Algorithm 8 */
const my_Fp t0 = a.squared();
const my_Fp t1 = b.squared();
const my_Fp t2 = t0 - non_residue * t1;
const my_Fp t3 = t2.inverse();
const my_Fp c0 = a * t3;
const my_Fp c1 = - (b * t3);
return Fp2_model<n,modulus>(c0, c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n,modulus> Fp2_model<n,modulus>::Frobenius_map(unsigned long power) const
{
return Fp2_model<n,modulus>(c0,
Frobenius_coeffs_c1[power % 2] * c1);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n,modulus> Fp2_model<n,modulus>::sqrt() const
{
if (is_zero()) {
return *this;
}
Fp2_model<n,modulus> one = Fp2_model<n,modulus>::one();
size_t v = Fp2_model<n,modulus>::s;
Fp2_model<n,modulus> z = Fp2_model<n,modulus>::nqr_to_t;
Fp2_model<n,modulus> w = (*this)^Fp2_model<n,modulus>::t_minus_1_over_2;
Fp2_model<n,modulus> x = (*this) * w;
Fp2_model<n,modulus> b = x * w; // b = (*this)^t
// check if square with euler's criterion
Fp2_model<n,modulus> check = b;
for (size_t i = 0; i < v-1; ++i)
{
check = check.squared();
}
if (check != one)
{
assert_except(0);
}
// compute square root with Tonelli--Shanks
// (does not terminate if not a square!)
while (b != one)
{
size_t m = 0;
Fp2_model<n,modulus> b2m = b;
while (b2m != one)
{
/* invariant: b2m = b^(2^m) after entering this loop */
b2m = b2m.squared();
m += 1;
}
int j = v-m-1;
w = z;
while (j > 0)
{
w = w.squared();
--j;
} // w = z^2^(v-m-1)
z = w.squared();
b = b * z;
x = x * w;
v = m;
}
return x;
}
template<mp_size_t n, const bigint<n>& modulus>
template<mp_size_t m>
Fp2_model<n,modulus> Fp2_model<n,modulus>::operator^(const bigint<m> &pow) const
{
return power<Fp2_model<n, modulus>, m>(*this, pow);
}
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream &out, const Fp2_model<n, modulus> &el)
{
out << el.c0 << OUTPUT_SEPARATOR << el.c1;
return out;
}
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream &in, Fp2_model<n, modulus> &el)
{
in >> el.c0 >> el.c1;
return in;
}
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream& out, const std::vector<Fp2_model<n, modulus> > &v)
{
out << v.size() << "\n";
for (const Fp2_model<n, modulus>& t : v)
{
out << t << OUTPUT_NEWLINE;
}
return out;
}
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream& in, std::vector<Fp2_model<n, modulus> > &v)
{
v.clear();
size_t s;
in >> s;
char b;
in.read(&b, 1);
v.reserve(s);
for (size_t i = 0; i < s; ++i)
{
Fp2_model<n, modulus> el;
in >> el;
v.emplace_back(el);
}
return in;
}
} // libsnark
#endif // FP2_TCC_

View File

@@ -0,0 +1,104 @@
/** @file
*****************************************************************************
Declaration of arithmetic in the finite field F[(p^2)^3]
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef FP6_3OVER2_HPP_
#define FP6_3OVER2_HPP_
#include "algebra/fields/fp.hpp"
#include "algebra/fields/fp2.hpp"
#include <vector>
namespace libsnark {
template<mp_size_t n, const bigint<n>& modulus>
class Fp6_3over2_model;
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream &, const Fp6_3over2_model<n, modulus> &);
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream &, Fp6_3over2_model<n, modulus> &);
/**
* Arithmetic in the finite field F[(p^2)^3].
*
* Let p := modulus. This interface provides arithmetic for the extension field
* Fp6 = Fp2[V]/(V^3-non_residue) where non_residue is in Fp.
*
* ASSUMPTION: p = 1 (mod 6)
*/
template<mp_size_t n, const bigint<n>& modulus>
class Fp6_3over2_model {
public:
typedef Fp_model<n, modulus> my_Fp;
typedef Fp2_model<n, modulus> my_Fp2;
static my_Fp2 non_residue;
static my_Fp2 Frobenius_coeffs_c1[6]; // non_residue^((modulus^i-1)/3) for i=0,1,2,3,4,5
static my_Fp2 Frobenius_coeffs_c2[6]; // non_residue^((2*modulus^i-2)/3) for i=0,1,2,3,4,5
my_Fp2 c0, c1, c2;
Fp6_3over2_model() {};
Fp6_3over2_model(const my_Fp2& c0, const my_Fp2& c1, const my_Fp2& c2) : c0(c0), c1(c1), c2(c2) {};
void clear() { c0.clear(); c1.clear(); c2.clear(); }
void print() const { printf("c0/c1/c2:\n"); c0.print(); c1.print(); c2.print(); }
static Fp6_3over2_model<n, modulus> zero();
static Fp6_3over2_model<n, modulus> one();
static Fp6_3over2_model<n, modulus> random_element();
bool is_zero() const { return c0.is_zero() && c1.is_zero() && c2.is_zero(); }
bool operator==(const Fp6_3over2_model &other) const;
bool operator!=(const Fp6_3over2_model &other) const;
Fp6_3over2_model operator+(const Fp6_3over2_model &other) const;
Fp6_3over2_model operator-(const Fp6_3over2_model &other) const;
Fp6_3over2_model operator*(const Fp6_3over2_model &other) const;
Fp6_3over2_model operator-() const;
Fp6_3over2_model squared() const;
Fp6_3over2_model inverse() const;
Fp6_3over2_model Frobenius_map(unsigned long power) const;
static my_Fp2 mul_by_non_residue(const my_Fp2 &elt);
template<mp_size_t m>
Fp6_3over2_model operator^(const bigint<m> &other) const;
static bigint<n> base_field_char() { return modulus; }
static size_t extension_degree() { return 6; }
friend std::ostream& operator<< <n, modulus>(std::ostream &out, const Fp6_3over2_model<n, modulus> &el);
friend std::istream& operator>> <n, modulus>(std::istream &in, Fp6_3over2_model<n, modulus> &el);
};
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream& out, const std::vector<Fp6_3over2_model<n, modulus> > &v);
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream& in, std::vector<Fp6_3over2_model<n, modulus> > &v);
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp6_3over2_model<n, modulus> &rhs);
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n, modulus> operator*(const Fp2_model<n, modulus> &lhs, const Fp6_3over2_model<n, modulus> &rhs);
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n, modulus> Fp6_3over2_model<n, modulus>::non_residue;
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n, modulus> Fp6_3over2_model<n, modulus>::Frobenius_coeffs_c1[6];
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n, modulus> Fp6_3over2_model<n, modulus>::Frobenius_coeffs_c2[6];
} // libsnark
#include "algebra/fields/fp6_3over2.tcc"
#endif // FP6_3OVER2_HPP_

View File

@@ -0,0 +1,216 @@
/** @file
*****************************************************************************
Implementation of arithmetic in the finite field F[(p^2)^3].
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef FP6_3OVER2_TCC_
#define FP6_3OVER2_TCC_
#include "algebra/fields/field_utils.hpp"
namespace libsnark {
template<mp_size_t n, const bigint<n>& modulus>
Fp2_model<n, modulus> Fp6_3over2_model<n,modulus>::mul_by_non_residue(const Fp2_model<n, modulus> &elt)
{
return Fp2_model<n, modulus>(non_residue * elt);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::zero()
{
return Fp6_3over2_model<n, modulus>(my_Fp2::zero(), my_Fp2::zero(), my_Fp2::zero());
}
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::one()
{
return Fp6_3over2_model<n, modulus>(my_Fp2::one(), my_Fp2::zero(), my_Fp2::zero());
}
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::random_element()
{
Fp6_3over2_model<n, modulus> r;
r.c0 = my_Fp2::random_element();
r.c1 = my_Fp2::random_element();
r.c2 = my_Fp2::random_element();
return r;
}
template<mp_size_t n, const bigint<n>& modulus>
bool Fp6_3over2_model<n,modulus>::operator==(const Fp6_3over2_model<n,modulus> &other) const
{
return (this->c0 == other.c0 && this->c1 == other.c1 && this->c2 == other.c2);
}
template<mp_size_t n, const bigint<n>& modulus>
bool Fp6_3over2_model<n,modulus>::operator!=(const Fp6_3over2_model<n,modulus> &other) const
{
return !(operator==(other));
}
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::operator+(const Fp6_3over2_model<n,modulus> &other) const
{
return Fp6_3over2_model<n,modulus>(this->c0 + other.c0,
this->c1 + other.c1,
this->c2 + other.c2);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::operator-(const Fp6_3over2_model<n,modulus> &other) const
{
return Fp6_3over2_model<n,modulus>(this->c0 - other.c0,
this->c1 - other.c1,
this->c2 - other.c2);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp6_3over2_model<n, modulus> &rhs)
{
return Fp6_3over2_model<n,modulus>(lhs*rhs.c0,
lhs*rhs.c1,
lhs*rhs.c2);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n, modulus> operator*(const Fp2_model<n, modulus> &lhs, const Fp6_3over2_model<n, modulus> &rhs)
{
return Fp6_3over2_model<n,modulus>(lhs*rhs.c0,
lhs*rhs.c1,
lhs*rhs.c2);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::operator*(const Fp6_3over2_model<n,modulus> &other) const
{
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 4 (Karatsuba) */
const my_Fp2 &A = other.c0, &B = other.c1, &C = other.c2,
&a = this->c0, &b = this->c1, &c = this->c2;
const my_Fp2 aA = a*A;
const my_Fp2 bB = b*B;
const my_Fp2 cC = c*C;
return Fp6_3over2_model<n,modulus>(aA + Fp6_3over2_model<n,modulus>::mul_by_non_residue((b+c)*(B+C)-bB-cC),
(a+b)*(A+B)-aA-bB+Fp6_3over2_model<n,modulus>::mul_by_non_residue(cC),
(a+c)*(A+C)-aA+bB-cC);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::operator-() const
{
return Fp6_3over2_model<n,modulus>(-this->c0,
-this->c1,
-this->c2);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::squared() const
{
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 4 (CH-SQR2) */
const my_Fp2 &a = this->c0, &b = this->c1, &c = this->c2;
const my_Fp2 s0 = a.squared();
const my_Fp2 ab = a*b;
const my_Fp2 s1 = ab + ab;
const my_Fp2 s2 = (a - b + c).squared();
const my_Fp2 bc = b*c;
const my_Fp2 s3 = bc + bc;
const my_Fp2 s4 = c.squared();
return Fp6_3over2_model<n,modulus>(s0 + Fp6_3over2_model<n,modulus>::mul_by_non_residue(s3),
s1 + Fp6_3over2_model<n,modulus>::mul_by_non_residue(s4),
s1 + s2 + s3 - s0 - s4);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::inverse() const
{
/* From "High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves"; Algorithm 17 */
const my_Fp2 &a = this->c0, &b = this->c1, &c = this->c2;
const my_Fp2 t0 = a.squared();
const my_Fp2 t1 = b.squared();
const my_Fp2 t2 = c.squared();
const my_Fp2 t3 = a*b;
const my_Fp2 t4 = a*c;
const my_Fp2 t5 = b*c;
const my_Fp2 c0 = t0 - Fp6_3over2_model<n,modulus>::mul_by_non_residue(t5);
const my_Fp2 c1 = Fp6_3over2_model<n,modulus>::mul_by_non_residue(t2) - t3;
const my_Fp2 c2 = t1 - t4; // typo in paper referenced above. should be "-" as per Scott, but is "*"
const my_Fp2 t6 = (a * c0 + Fp6_3over2_model<n,modulus>::mul_by_non_residue((c * c1 + b * c2))).inverse();
return Fp6_3over2_model<n,modulus>(t6 * c0, t6 * c1, t6 * c2);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::Frobenius_map(unsigned long power) const
{
return Fp6_3over2_model<n,modulus>(c0.Frobenius_map(power),
Frobenius_coeffs_c1[power % 6] * c1.Frobenius_map(power),
Frobenius_coeffs_c2[power % 6] * c2.Frobenius_map(power));
}
template<mp_size_t n, const bigint<n>& modulus>
template<mp_size_t m>
Fp6_3over2_model<n,modulus> Fp6_3over2_model<n,modulus>::operator^(const bigint<m> &pow) const
{
return power<Fp6_3over2_model<n, modulus>, m>(*this, pow);
}
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream &out, const Fp6_3over2_model<n, modulus> &el)
{
out << el.c0 << OUTPUT_SEPARATOR << el.c1 << OUTPUT_SEPARATOR << el.c2;
return out;
}
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream &in, Fp6_3over2_model<n, modulus> &el)
{
in >> el.c0 >> el.c1 >> el.c2;
return in;
}
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream& out, const std::vector<Fp6_3over2_model<n, modulus> > &v)
{
out << v.size() << "\n";
for (const Fp6_3over2_model<n, modulus>& t : v)
{
out << t << OUTPUT_NEWLINE;
}
return out;
}
template<mp_size_t n, const bigint<n>& modulus>
std::istream& operator>>(std::istream& in, std::vector<Fp6_3over2_model<n, modulus> > &v)
{
v.clear();
size_t s;
in >> s;
char b;
in.read(&b, 1);
v.reserve(s);
for (size_t i = 0; i < s; ++i)
{
Fp6_3over2_model<n, modulus> el;
in >> el;
v.emplace_back(el);
}
return in;
}
} // libsnark
#endif // FP6_3_OVER_2_TCC_

View File

@@ -0,0 +1,389 @@
/** @file
*****************************************************************************
Assembly code snippets for F[p] finite field arithmetic, used by fp.tcc .
Specific to x86-64, and used only if USE_ASM is defined.
On other architectures or without USE_ASM, fp.tcc uses a portable
C++ implementation instead.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef FP_AUX_TCC_
#define FP_AUX_TCC_
namespace libsnark {
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
/* addq is faster than adcq, even if preceded by clc */
#define ADD_FIRSTADD \
"movq (%[B]), %%rax \n\t" \
"addq %%rax, (%[A]) \n\t"
#define ADD_NEXTADD(ofs) \
"movq " STR(ofs) "(%[B]), %%rax \n\t" \
"adcq %%rax, " STR(ofs) "(%[A]) \n\t"
#define ADD_CMP(ofs) \
"movq " STR(ofs) "(%[mod]), %%rax \n\t" \
"cmpq %%rax, " STR(ofs) "(%[A]) \n\t" \
"jb done%= \n\t" \
"ja subtract%= \n\t"
#define ADD_FIRSTSUB \
"movq (%[mod]), %%rax \n\t" \
"subq %%rax, (%[A]) \n\t"
#define ADD_FIRSTSUB \
"movq (%[mod]), %%rax \n\t" \
"subq %%rax, (%[A]) \n\t"
#define ADD_NEXTSUB(ofs) \
"movq " STR(ofs) "(%[mod]), %%rax \n\t" \
"sbbq %%rax, " STR(ofs) "(%[A]) \n\t"
#define SUB_FIRSTSUB \
"movq (%[B]), %%rax\n\t" \
"subq %%rax, (%[A])\n\t"
#define SUB_NEXTSUB(ofs) \
"movq " STR(ofs) "(%[B]), %%rax\n\t" \
"sbbq %%rax, " STR(ofs) "(%[A])\n\t"
#define SUB_FIRSTADD \
"movq (%[mod]), %%rax\n\t" \
"addq %%rax, (%[A])\n\t"
#define SUB_NEXTADD(ofs) \
"movq " STR(ofs) "(%[mod]), %%rax\n\t" \
"adcq %%rax, " STR(ofs) "(%[A])\n\t"
#define MONT_CMP(ofs) \
"movq " STR(ofs) "(%[M]), %%rax \n\t" \
"cmpq %%rax, " STR(ofs) "(%[tmp]) \n\t" \
"jb done%= \n\t" \
"ja subtract%= \n\t"
#define MONT_FIRSTSUB \
"movq (%[M]), %%rax \n\t" \
"subq %%rax, (%[tmp]) \n\t"
#define MONT_NEXTSUB(ofs) \
"movq " STR(ofs) "(%[M]), %%rax \n\t" \
"sbbq %%rax, " STR(ofs) "(%[tmp]) \n\t"
/*
The x86-64 Montgomery multiplication here is similar
to Algorithm 2 (CIOS method) in http://eprint.iacr.org/2012/140.pdf
and the PowerPC pseudocode of gmp-ecm library (c) Paul Zimmermann and Alexander Kruppa
(see comments on top of powerpc64/mulredc.m4).
*/
#define MONT_PRECOMPUTE \
"xorq %[cy], %[cy] \n\t" \
"movq 0(%[A]), %%rax \n\t" \
"mulq 0(%[B]) \n\t" \
"movq %%rax, %[T0] \n\t" \
"movq %%rdx, %[T1] # T1:T0 <- A[0] * B[0] \n\t" \
"mulq %[inv] \n\t" \
"movq %%rax, %[u] # u <- T0 * inv \n\t" \
"mulq 0(%[M]) \n\t" \
"addq %[T0], %%rax \n\t" \
"adcq %%rdx, %[T1] \n\t" \
"adcq $0, %[cy] # cy:T1 <- (M[0]*u + T1 * b + T0) / b\n\t"
#define MONT_FIRSTITER(j) \
"xorq %[T0], %[T0] \n\t" \
"movq 0(%[A]), %%rax \n\t" \
"mulq " STR((j*8)) "(%[B]) \n\t" \
"addq %[T1], %%rax \n\t" \
"movq %%rax, " STR(((j-1)*8)) "(%[tmp]) \n\t" \
"adcq $0, %%rdx \n\t" \
"movq %%rdx, %[T1] # now T1:tmp[j-1] <-- X[0] * Y[j] + T1\n\t" \
"movq " STR((j*8)) "(%[M]), %%rax \n\t" \
"mulq %[u] \n\t" \
"addq %%rax, " STR(((j-1)*8)) "(%[tmp]) \n\t" \
"adcq %[cy], %%rdx \n\t" \
"adcq $0, %[T0] \n\t" \
"xorq %[cy], %[cy] \n\t" \
"addq %%rdx, %[T1] \n\t" \
"adcq %[T0], %[cy] # cy:T1:tmp[j-1] <---- (X[0] * Y[j] + T1) + (M[j] * u + cy * b) \n\t"
#define MONT_ITERFIRST(i) \
"xorq %[cy], %[cy] \n\t" \
"movq " STR((i*8)) "(%[A]), %%rax \n\t" \
"mulq 0(%[B]) \n\t" \
"addq 0(%[tmp]), %%rax \n\t" \
"adcq 8(%[tmp]), %%rdx \n\t" \
"adcq $0, %[cy] \n\t" \
"movq %%rax, %[T0] \n\t" \
"movq %%rdx, %[T1] # cy:T1:T0 <- A[i] * B[0] + tmp[1] * b + tmp[0]\n\t" \
"mulq %[inv] \n\t" \
"movq %%rax, %[u] # u <- T0 * inv\n\t" \
"mulq 0(%[M]) \n\t" \
"addq %[T0], %%rax \n\t" \
"adcq %%rdx, %[T1] \n\t" \
"adcq $0, %[cy] # cy:T1 <- (M[0]*u + cy * b * b + T1 * b + T0) / b\n\t"
#define MONT_ITERITER(i, j) \
"xorq %[T0], %[T0] \n\t" \
"movq " STR((i*8)) "(%[A]), %%rax \n\t" \
"mulq " STR((j*8)) "(%[B]) \n\t" \
"addq %[T1], %%rax \n\t" \
"movq %%rax, " STR(((j-1)*8)) "(%[tmp]) \n\t" \
"adcq $0, %%rdx \n\t" \
"movq %%rdx, %[T1] # now T1:tmp[j-1] <-- X[i] * Y[j] + T1 \n\t" \
"movq " STR((j*8)) "(%[M]), %%rax \n\t" \
"mulq %[u] \n\t" \
"addq %%rax, " STR(((j-1)*8)) "(%[tmp]) \n\t" \
"adcq %[cy], %%rdx \n\t" \
"adcq $0, %[T0] \n\t" \
"xorq %[cy], %[cy] \n\t" \
"addq %%rdx, %[T1] \n\t" \
"adcq %[T0], %[cy] # cy:T1:tmp[j-1] <-- (X[i] * Y[j] + T1) + M[j] * u + cy * b \n\t" \
"addq " STR(((j+1)*8)) "(%[tmp]), %[T1] \n\t" \
"adcq $0, %[cy] # cy:T1:tmp[j-1] <-- (X[i] * Y[j] + T1) + M[j] * u + (tmp[j+1] + cy) * b \n\t"
#define MONT_FINALIZE(j) \
"movq %[T1], " STR((j*8)) "(%[tmp]) \n\t" \
"movq %[cy], " STR(((j+1)*8)) "(%[tmp]) \n\t"
/*
Comba multiplication and squaring routines are based on the
public-domain tomsfastmath library by Tom St Denis
<http://www.libtom.org/>
<https://github.com/libtom/tomsfastmath/blob/master/src/sqr/fp_sqr_comba.c
<https://github.com/libtom/tomsfastmath/blob/master/src/mul/fp_mul_comba.c>
Compared to the above, we save 5-20% of cycles by using careful register
renaming to implement Comba forward operation.
*/
#define COMBA_3_BY_3_MUL(c0_, c1_, c2_, res_, A_, B_) \
asm volatile ( \
"movq 0(%[A]), %%rax \n\t" \
"mulq 0(%[B]) \n\t" \
"movq %%rax, 0(%[res]) \n\t" \
"movq %%rdx, %[c0] \n\t" \
\
"xorq %[c1], %[c1] \n\t" \
"movq 0(%[A]), %%rax \n\t" \
"mulq 8(%[B]) \n\t" \
"addq %%rax, %[c0] \n\t" \
"adcq %%rdx, %[c1] \n\t" \
\
"xorq %[c2], %[c2] \n\t" \
"movq 8(%[A]), %%rax \n\t" \
"mulq 0(%[B]) \n\t" \
"addq %%rax, %[c0] \n\t" \
"movq %[c0], 8(%[res]) \n\t" \
"adcq %%rdx, %[c1] \n\t" \
"adcq $0, %[c2] \n\t" \
\
"// register renaming (c1, c2, c0)\n\t" \
"xorq %[c0], %[c0] \n\t" \
"movq 0(%[A]), %%rax \n\t" \
"mulq 16(%[B]) \n\t" \
"addq %%rax, %[c1] \n\t" \
"adcq %%rdx, %[c2] \n\t" \
"adcq $0, %[c0] \n\t" \
\
"movq 8(%[A]), %%rax \n\t" \
"mulq 8(%[B]) \n\t" \
"addq %%rax, %[c1] \n\t" \
"adcq %%rdx, %[c2] \n\t" \
"adcq $0, %[c0] \n\t" \
\
"movq 16(%[A]), %%rax \n\t" \
"mulq 0(%[B]) \n\t" \
"addq %%rax, %[c1] \n\t" \
"movq %[c1], 16(%[res]) \n\t" \
"adcq %%rdx, %[c2] \n\t" \
"adcq $0, %[c0] \n\t" \
\
"// register renaming (c2, c0, c1)\n\t" \
"xorq %[c1], %[c1] \n\t" \
"movq 8(%[A]), %%rax \n\t" \
"mulq 16(%[B]) \n\t" \
"addq %%rax, %[c2] \n\t" \
"adcq %%rdx, %[c0] \n\t" \
"adcq $0, %[c1] \n\t" \
\
"movq 16(%[A]), %%rax \n\t" \
"mulq 8(%[B]) \n\t" \
"addq %%rax, %[c2] \n\t" \
"movq %[c2], 24(%[res]) \n\t" \
"adcq %%rdx, %[c0] \n\t" \
"adcq $0, %[c1] \n\t" \
\
"// register renaming (c0, c1, c2)\n\t" \
"xorq %[c2], %[c2] \n\t" \
"movq 16(%[A]), %%rax \n\t" \
"mulq 16(%[B]) \n\t" \
"addq %%rax, %[c0] \n\t" \
"movq %[c0], 32(%[res]) \n\t" \
"adcq %%rdx, %[c1] \n\t" \
"movq %[c1], 40(%[res]) \n\t" \
: [c0] "=&r" (c0_), [c1] "=&r" (c1_), [c2] "=&r" (c2_) \
: [res] "r" (res_), [A] "r" (A_), [B] "r" (B_) \
: "%rax", "%rdx", "cc", "memory")
#define COMBA_3_BY_3_SQR(c0_, c1_, c2_, res_, A_) \
asm volatile ( \
"xorq %[c1], %[c1] \n\t" \
"xorq %[c2], %[c2] \n\t" \
"movq 0(%[A]), %%rax \n\t" \
"mulq %%rax \n\t" \
"movq %%rax, 0(%[res]) \n\t" \
"movq %%rdx, %[c0] \n\t" \
\
"movq 0(%[A]), %%rax \n\t" \
"mulq 8(%[A]) \n\t" \
"addq %%rax, %[c0] \n\t" \
"adcq %%rdx, %[c1] \n\t" \
"addq %%rax, %[c0] \n\t" \
"movq %[c0], 8(%[res]) \n\t" \
"adcq %%rdx, %[c1] \n\t" \
"adcq $0, %[c2] \n\t" \
\
"// register renaming (c1, c2, c0)\n\t" \
"movq 0(%[A]), %%rax \n\t" \
"xorq %[c0], %[c0] \n\t" \
"mulq 16(%[A]) \n\t" \
"addq %%rax, %[c1] \n\t" \
"adcq %%rdx, %[c2] \n\t" \
"adcq $0, %[c0] \n\t" \
"addq %%rax, %[c1] \n\t" \
"adcq %%rdx, %[c2] \n\t" \
"adcq $0, %[c0] \n\t" \
\
"movq 8(%[A]), %%rax \n\t" \
"mulq %%rax \n\t" \
"addq %%rax, %[c1] \n\t" \
"movq %[c1], 16(%[res]) \n\t" \
"adcq %%rdx, %[c2] \n\t" \
"adcq $0, %[c0] \n\t" \
\
"// register renaming (c2, c0, c1)\n\t" \
"movq 8(%[A]), %%rax \n\t" \
"xorq %[c1], %[c1] \n\t" \
"mulq 16(%[A]) \n\t" \
"addq %%rax, %[c2] \n\t" \
"adcq %%rdx, %[c0] \n\t" \
"adcq $0, %[c1] \n\t" \
"addq %%rax, %[c2] \n\t" \
"movq %[c2], 24(%[res]) \n\t" \
"adcq %%rdx, %[c0] \n\t" \
"adcq $0, %[c1] \n\t" \
\
"// register renaming (c0, c1, c2)\n\t" \
"movq 16(%[A]), %%rax \n\t" \
"mulq %%rax \n\t" \
"addq %%rax, %[c0] \n\t" \
"movq %[c0], 32(%[res]) \n\t" \
"adcq %%rdx, %[c1] \n\t" \
"movq %[c1], 40(%[res]) \n\t" \
\
: [c0] "=&r" (c0_), [c1] "=&r" (c1_), [c2] "=&r" (c2_) \
: [res] "r" (res_), [A] "r" (A_) \
: "%rax", "%rdx", "cc", "memory")
/*
The Montgomery reduction here is based on Algorithm 14.32 in
Handbook of Applied Cryptography
<http://cacr.uwaterloo.ca/hac/about/chap14.pdf>.
*/
#define REDUCE_6_LIMB_PRODUCT(k_, tmp1_, tmp2_, tmp3_, inv_, res_, mod_) \
__asm__ volatile \
("///////////////////////////////////\n\t" \
"movq 0(%[res]), %%rax \n\t" \
"mulq %[modprime] \n\t" \
"movq %%rax, %[k] \n\t" \
\
"movq (%[mod]), %%rax \n\t" \
"mulq %[k] \n\t" \
"movq %%rax, %[tmp1] \n\t" \
"movq %%rdx, %[tmp2] \n\t" \
\
"xorq %[tmp3], %[tmp3] \n\t" \
"movq 8(%[mod]), %%rax \n\t" \
"mulq %[k] \n\t" \
"addq %[tmp1], 0(%[res]) \n\t" \
"adcq %%rax, %[tmp2] \n\t" \
"adcq %%rdx, %[tmp3] \n\t" \
\
"xorq %[tmp1], %[tmp1] \n\t" \
"movq 16(%[mod]), %%rax \n\t" \
"mulq %[k] \n\t" \
"addq %[tmp2], 8(%[res]) \n\t" \
"adcq %%rax, %[tmp3] \n\t" \
"adcq %%rdx, %[tmp1] \n\t" \
\
"addq %[tmp3], 16(%[res]) \n\t" \
"adcq %[tmp1], 24(%[res]) \n\t" \
"adcq $0, 32(%[res]) \n\t" \
"adcq $0, 40(%[res]) \n\t" \
\
"///////////////////////////////////\n\t" \
"movq 8(%[res]), %%rax \n\t" \
"mulq %[modprime] \n\t" \
"movq %%rax, %[k] \n\t" \
\
"movq (%[mod]), %%rax \n\t" \
"mulq %[k] \n\t" \
"movq %%rax, %[tmp1] \n\t" \
"movq %%rdx, %[tmp2] \n\t" \
\
"xorq %[tmp3], %[tmp3] \n\t" \
"movq 8(%[mod]), %%rax \n\t" \
"mulq %[k] \n\t" \
"addq %[tmp1], 8(%[res]) \n\t" \
"adcq %%rax, %[tmp2] \n\t" \
"adcq %%rdx, %[tmp3] \n\t" \
\
"xorq %[tmp1], %[tmp1] \n\t" \
"movq 16(%[mod]), %%rax \n\t" \
"mulq %[k] \n\t" \
"addq %[tmp2], 16(%[res]) \n\t" \
"adcq %%rax, %[tmp3] \n\t" \
"adcq %%rdx, %[tmp1] \n\t" \
\
"addq %[tmp3], 24(%[res]) \n\t" \
"adcq %[tmp1], 32(%[res]) \n\t" \
"adcq $0, 40(%[res]) \n\t" \
\
"///////////////////////////////////\n\t" \
"movq 16(%[res]), %%rax \n\t" \
"mulq %[modprime] \n\t" \
"movq %%rax, %[k] \n\t" \
\
"movq (%[mod]), %%rax \n\t" \
"mulq %[k] \n\t" \
"movq %%rax, %[tmp1] \n\t" \
"movq %%rdx, %[tmp2] \n\t" \
\
"xorq %[tmp3], %[tmp3] \n\t" \
"movq 8(%[mod]), %%rax \n\t" \
"mulq %[k] \n\t" \
"addq %[tmp1], 16(%[res]) \n\t" \
"adcq %%rax, %[tmp2] \n\t" \
"adcq %%rdx, %[tmp3] \n\t" \
\
"xorq %[tmp1], %[tmp1] \n\t" \
"movq 16(%[mod]), %%rax \n\t" \
"mulq %[k] \n\t" \
"addq %[tmp2], 24(%[res]) \n\t" \
"adcq %%rax, %[tmp3] \n\t" \
"adcq %%rdx, %[tmp1] \n\t" \
\
"addq %[tmp3], 32(%[res]) \n\t" \
"adcq %[tmp1], 40(%[res]) \n\t" \
: [k] "=&r" (k_), [tmp1] "=&r" (tmp1_), [tmp2] "=&r" (tmp2_), [tmp3] "=&r" (tmp3_) \
: [modprime] "r" (inv_), [res] "r" (res_), [mod] "r" (mod_) \
: "%rax", "%rdx", "cc", "memory")
} // libsnark
#endif // FP_AUX_TCC_

View File

@@ -0,0 +1,97 @@
/**
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "algebra/fields/bigint.hpp"
#include <gtest/gtest.h>
using namespace libsnark;
TEST(algebra, bigint)
{
static_assert(ULONG_MAX == 0xFFFFFFFFFFFFFFFFul, "unsigned long not 64-bit");
static_assert(GMP_NUMB_BITS == 64, "GMP limb not 64-bit");
const char *b1_decimal = "76749407";
const char *b2_decimal = "435020359732196472065729437602";
const char *b3_decimal = "33387554642372758038536799358397002014";
const char *b2_binary = "0000000000000000000000000000010101111101101000000110100001011010"
"1101101010001001000001101000101000100110011001110001111110100010";
bigint<1> b0 = bigint<1>(0ul);
bigint<1> b1 = bigint<1>(b1_decimal);
bigint<2> b2 = bigint<2>(b2_decimal);
EXPECT_EQ(b0.as_ulong(), 0ul);
EXPECT_TRUE(b0.is_zero());
EXPECT_EQ(b1.as_ulong(), 76749407ul);
EXPECT_FALSE(b1.is_zero());
EXPECT_EQ(b2.as_ulong(), 15747124762497195938ul);
EXPECT_FALSE(b2.is_zero());
EXPECT_NE(b0, b1);
EXPECT_FALSE(b0 == b1);
EXPECT_EQ(b2.max_bits(), 128);
EXPECT_EQ(b2.num_bits(), 99);
for (size_t i = 0; i < 128; i++) {
EXPECT_EQ(b2.test_bit(i), (b2_binary[127-i] == '1'));
}
bigint<3> b3 = b2 * b1;
EXPECT_EQ(b3, bigint<3>(b3_decimal));
EXPECT_FALSE(b3.is_zero());
bigint<3> b3a { b3 };
EXPECT_EQ(b3a, bigint<3>(b3_decimal));
EXPECT_EQ(b3a, b3);
EXPECT_FALSE(b3a.is_zero());
mpz_t m3;
mpz_init(m3);
b3.to_mpz(m3);
bigint<3> b3b { m3 };
EXPECT_EQ(b3b, b3);
bigint<2> quotient;
bigint<2> remainder;
bigint<3>::div_qr(quotient, remainder, b3, b2);
EXPECT_LT(quotient.num_bits(), GMP_NUMB_BITS);
EXPECT_EQ(quotient.as_ulong(), b1.as_ulong());
bigint<1> b1inc = bigint<1>("76749408");
bigint<1> b1a = quotient.shorten(b1inc, "test");
EXPECT_EQ(b1a, b1);
EXPECT_TRUE(remainder.is_zero());
remainder.limit(b2, "test");
EXPECT_THROW((void)(quotient.shorten(b1, "test")), std::domain_error);
EXPECT_THROW(remainder.limit(remainder, "test"), std::domain_error);
bigint<1> br = bigint<1>("42");
b3 += br;
EXPECT_NE(b3, b3a);
EXPECT_GT(b3, b3a);
EXPECT_FALSE(b3a > b3);
bigint<3>::div_qr(quotient, remainder, b3, b2);
EXPECT_LT(quotient.num_bits(), GMP_NUMB_BITS);
EXPECT_EQ(quotient.as_ulong(), b1.as_ulong());
EXPECT_LT(remainder.num_bits(), GMP_NUMB_BITS);
EXPECT_EQ(remainder.as_ulong(), 42);
b3a.clear();
EXPECT_TRUE(b3a.is_zero());
EXPECT_EQ(b3a.num_bits(), 0);
EXPECT_FALSE(b3.is_zero());
bigint<4> bx = bigint<4>().randomize();
bigint<4> by = bigint<4>().randomize();
EXPECT_FALSE(bx == by);
// TODO: test serialization
}

View File

@@ -0,0 +1,191 @@
/**
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#include "common/profiling.hpp"
#ifdef CURVE_BN128
#include "algebra/curves/bn128/bn128_pp.hpp"
#endif
#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp"
#include "algebra/fields/fp6_3over2.hpp"
#include "algebra/fields/fp12_2over3over2.hpp"
#include <gtest/gtest.h>
using namespace libsnark;
template<typename FieldT>
void test_field()
{
bigint<1> rand1 = bigint<1>("76749407");
bigint<1> rand2 = bigint<1>("44410867");
bigint<1> randsum = bigint<1>("121160274");
FieldT zero = FieldT::zero();
FieldT one = FieldT::one();
FieldT a = FieldT::random_element();
FieldT a_ser;
a_ser = reserialize<FieldT>(a);
EXPECT_EQ(a_ser, a);
FieldT b = FieldT::random_element();
FieldT c = FieldT::random_element();
FieldT d = FieldT::random_element();
EXPECT_NE(a, zero);
EXPECT_NE(a, one);
EXPECT_EQ(a * a, a.squared());
EXPECT_EQ((a + b).squared(), a.squared() + a*b + b*a + b.squared());
EXPECT_EQ((a + b)*(c + d), a*c + a*d + b*c + b*d);
EXPECT_EQ(a - b, a + (-b));
EXPECT_EQ(a - b, (-b) + a);
EXPECT_EQ((a ^ rand1) * (a ^ rand2), (a^randsum));
EXPECT_EQ(a * a.inverse(), one);
EXPECT_EQ((a + b) * c.inverse(), a * c.inverse() + (b.inverse() * c).inverse());
}
template<typename FieldT>
void test_sqrt()
{
for (size_t i = 0; i < 100; ++i)
{
FieldT a = FieldT::random_element();
FieldT asq = a.squared();
EXPECT_TRUE(asq.sqrt() == a || asq.sqrt() == -a);
}
}
template<typename FieldT>
void test_two_squarings()
{
FieldT a = FieldT::random_element();
EXPECT_EQ(a.squared(), a * a);
EXPECT_EQ(a.squared(), a.squared_complex());
EXPECT_EQ(a.squared(), a.squared_karatsuba());
}
template<typename FieldT>
void test_Frobenius()
{
FieldT a = FieldT::random_element();
EXPECT_EQ(a.Frobenius_map(0), a);
FieldT a_q = a ^ FieldT::base_field_char();
for (size_t power = 1; power < 10; ++power)
{
const FieldT a_qi = a.Frobenius_map(power);
EXPECT_EQ(a_qi, a_q);
a_q = a_q ^ FieldT::base_field_char();
}
}
template<typename FieldT>
void test_unitary_inverse()
{
EXPECT_EQ(FieldT::extension_degree() % 2, 0);
FieldT a = FieldT::random_element();
FieldT aqcubed_minus1 = a.Frobenius_map(FieldT::extension_degree()/2) * a.inverse();
EXPECT_EQ(aqcubed_minus1.inverse(), aqcubed_minus1.unitary_inverse());
}
template<typename ppT>
void test_all_fields()
{
test_field<Fr<ppT> >();
test_field<Fq<ppT> >();
test_field<Fqe<ppT> >();
test_field<Fqk<ppT> >();
test_sqrt<Fr<ppT> >();
test_sqrt<Fq<ppT> >();
test_sqrt<Fqe<ppT> >();
test_Frobenius<Fqe<ppT> >();
test_Frobenius<Fqk<ppT> >();
test_unitary_inverse<Fqk<ppT> >();
}
template<typename Fp4T>
void test_Fp4_tom_cook()
{
typedef typename Fp4T::my_Fp FieldT;
for (size_t i = 0; i < 100; ++i)
{
const Fp4T a = Fp4T::random_element();
const Fp4T b = Fp4T::random_element();
const Fp4T correct_res = a * b;
Fp4T res;
const FieldT
&a0 = a.c0.c0,
&a1 = a.c1.c0,
&a2 = a.c0.c1,
&a3 = a.c1.c1;
const FieldT
&b0 = b.c0.c0,
&b1 = b.c1.c0,
&b2 = b.c0.c1,
&b3 = b.c1.c1;
FieldT
&c0 = res.c0.c0,
&c1 = res.c1.c0,
&c2 = res.c0.c1,
&c3 = res.c1.c1;
const FieldT v0 = a0 * b0;
const FieldT v1 = (a0 + a1 + a2 + a3) * (b0 + b1 + b2 + b3);
const FieldT v2 = (a0 - a1 + a2 - a3) * (b0 - b1 + b2 - b3);
const FieldT v3 = (a0 + FieldT(2)*a1 + FieldT(4)*a2 + FieldT(8)*a3) * (b0 + FieldT(2)*b1 + FieldT(4)*b2 + FieldT(8)*b3);
const FieldT v4 = (a0 - FieldT(2)*a1 + FieldT(4)*a2 - FieldT(8)*a3) * (b0 - FieldT(2)*b1 + FieldT(4)*b2 - FieldT(8)*b3);
const FieldT v5 = (a0 + FieldT(3)*a1 + FieldT(9)*a2 + FieldT(27)*a3) * (b0 + FieldT(3)*b1 + FieldT(9)*b2 + FieldT(27)*b3);
const FieldT v6 = a3 * b3;
const FieldT beta = Fp4T::non_residue;
c0 = v0 + beta*(FieldT(4).inverse()*v0 - FieldT(6).inverse()*(v1 + v2) + FieldT(24).inverse() * (v3 + v4) - FieldT(5) * v6);
c1 = - FieldT(3).inverse()*v0 + v1 - FieldT(2).inverse()*v2 - FieldT(4).inverse()*v3 + FieldT(20).inverse() * v4 + FieldT(30).inverse() * v5 - FieldT(12) * v6 + beta * ( - FieldT(12).inverse() * (v0 - v1) + FieldT(24).inverse()*(v2 - v3) - FieldT(120).inverse() * (v4 - v5) - FieldT(3) * v6);
c2 = - (FieldT(5)*(FieldT(4).inverse()))* v0 + (FieldT(2)*(FieldT(3).inverse()))*(v1 + v2) - FieldT(24).inverse()*(v3 + v4) + FieldT(4)*v6 + beta*v6;
c3 = FieldT(12).inverse() * (FieldT(5)*v0 - FieldT(7)*v1) - FieldT(24).inverse()*(v2 - FieldT(7)*v3 + v4 + v5) + FieldT(15)*v6;
EXPECT_EQ(res, correct_res);
// {v0, v3, v4, v5}
const FieldT u = (FieldT::one() - beta).inverse();
EXPECT_EQ(v0, u * c0 + beta * u * c2 - beta * u * FieldT(2).inverse() * v1 - beta * u * FieldT(2).inverse() * v2 + beta * v6);
EXPECT_EQ(v3, - FieldT(15) * u * c0 - FieldT(30) * u * c1 - FieldT(3) * (FieldT(4) + beta) * u * c2 - FieldT(6) * (FieldT(4) + beta) * u * c3 + (FieldT(24) - FieldT(3) * beta * FieldT(2).inverse()) * u * v1 + (-FieldT(8) + beta * FieldT(2).inverse()) * u * v2
- FieldT(3) * (-FieldT(16) + beta) * v6);
EXPECT_EQ(v4, - FieldT(15) * u * c0 + FieldT(30) * u * c1 - FieldT(3) * (FieldT(4) + beta) * u * c2 + FieldT(6) * (FieldT(4) + beta) * u * c3 + (FieldT(24) - FieldT(3) * beta * FieldT(2).inverse()) * u * v2 + (-FieldT(8) + beta * FieldT(2).inverse()) * u * v1
- FieldT(3) * (-FieldT(16) + beta) * v6);
EXPECT_EQ(v5, - FieldT(80) * u * c0 - FieldT(240) * u * c1 - FieldT(8) * (FieldT(9) + beta) * u * c2 - FieldT(24) * (FieldT(9) + beta) * u * c3 - FieldT(2) * (-FieldT(81) + beta) * u * v1 + (-FieldT(81) + beta) * u * v2
- FieldT(8) * (-FieldT(81) + beta) * v6);
// c0 + beta c2 - (beta v1)/2 - (beta v2)/ 2 - (-1 + beta) beta v6,
// -15 c0 - 30 c1 - 3 (4 + beta) c2 - 6 (4 + beta) c3 + (24 - (3 beta)/2) v1 + (-8 + beta/2) v2 + 3 (-16 + beta) (-1 + beta) v6,
// -15 c0 + 30 c1 - 3 (4 + beta) c2 + 6 (4 + beta) c3 + (-8 + beta/2) v1 + (24 - (3 beta)/2) v2 + 3 (-16 + beta) (-1 + beta) v6,
// -80 c0 - 240 c1 - 8 (9 + beta) c2 - 24 (9 + beta) c3 - 2 (-81 + beta) v1 + (-81 + beta) v2 + 8 (-81 + beta) (-1 + beta) v6
}
}
TEST(algebra, fields)
{
alt_bn128_pp::init_public_params();
test_field<alt_bn128_Fq6>();
test_Frobenius<alt_bn128_Fq6>();
test_all_fields<alt_bn128_pp>();
#ifdef CURVE_BN128 // BN128 has fancy dependencies so it may be disabled
bn128_pp::init_public_params();
test_field<Fr<bn128_pp> >();
test_field<Fq<bn128_pp> >();
#endif
}

View File

@@ -0,0 +1,84 @@
/** @file
*****************************************************************************
Declaration of interfaces for:
- a knowledge commitment, and
- a knowledge commitment vector.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef KNOWLEDGE_COMMITMENT_HPP_
#define KNOWLEDGE_COMMITMENT_HPP_
#include "algebra/fields/fp.hpp"
#include "common/data_structures/sparse_vector.hpp"
namespace libsnark {
/********************** Knowledge commitment *********************************/
/**
* A knowledge commitment is a pair (g,h) where g is in T1 and h in T2,
* and T1 and T2 are groups (written additively).
*
* Such pairs form a group by defining:
* - "zero" = (0,0)
* - "one" = (1,1)
* - a * (g,h) + b * (g',h') := ( a * g + b * g', a * h + b * h').
*/
template<typename T1, typename T2>
struct knowledge_commitment {
T1 g;
T2 h;
knowledge_commitment<T1,T2>() = default;
knowledge_commitment<T1,T2>(const knowledge_commitment<T1,T2> &other) = default;
knowledge_commitment<T1,T2>(knowledge_commitment<T1,T2> &&other) = default;
knowledge_commitment<T1,T2>(const T1 &g, const T2 &h);
knowledge_commitment<T1,T2>& operator=(const knowledge_commitment<T1,T2> &other) = default;
knowledge_commitment<T1,T2>& operator=(knowledge_commitment<T1,T2> &&other) = default;
knowledge_commitment<T1,T2> operator+(const knowledge_commitment<T1, T2> &other) const;
bool is_zero() const;
bool operator==(const knowledge_commitment<T1,T2> &other) const;
bool operator!=(const knowledge_commitment<T1,T2> &other) const;
static knowledge_commitment<T1,T2> zero();
static knowledge_commitment<T1,T2> one();
void print() const;
static size_t size_in_bits();
};
template<typename T1, typename T2, mp_size_t m>
knowledge_commitment<T1,T2> operator*(const bigint<m> &lhs, const knowledge_commitment<T1,T2> &rhs);
template<typename T1, typename T2, mp_size_t m, const bigint<m> &modulus_p>
knowledge_commitment<T1,T2> operator*(const Fp_model<m, modulus_p> &lhs, const knowledge_commitment<T1,T2> &rhs);
template<typename T1,typename T2>
std::ostream& operator<<(std::ostream& out, const knowledge_commitment<T1,T2> &kc);
template<typename T1,typename T2>
std::istream& operator>>(std::istream& in, knowledge_commitment<T1,T2> &kc);
/******************** Knowledge commitment vector ****************************/
/**
* A knowledge commitment vector is a sparse vector of knowledge commitments.
*/
template<typename T1, typename T2>
using knowledge_commitment_vector = sparse_vector<knowledge_commitment<T1, T2> >;
} // libsnark
#include "algebra/knowledge_commitment/knowledge_commitment.tcc"
#endif // KNOWLEDGE_COMMITMENT_HPP_

View File

@@ -0,0 +1,111 @@
/** @file
*****************************************************************************
Implementation of interfaces for:
- a knowledge commitment, and
- a knowledge commitment vector.
See knowledge_commitment.hpp .
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef KNOWLEDGE_COMMITMENT_TCC_
#define KNOWLEDGE_COMMITMENT_TCC_
namespace libsnark {
template<typename T1, typename T2>
knowledge_commitment<T1,T2>::knowledge_commitment(const T1 &g, const T2 &h) :
g(g), h(h)
{
}
template<typename T1, typename T2>
knowledge_commitment<T1,T2> knowledge_commitment<T1,T2>::zero()
{
return knowledge_commitment<T1,T2>(T1::zero(), T2::zero());
}
template<typename T1, typename T2>
knowledge_commitment<T1,T2> knowledge_commitment<T1,T2>::one()
{
return knowledge_commitment<T1,T2>(T1::one(), T2::one());
}
template<typename T1, typename T2>
knowledge_commitment<T1,T2> knowledge_commitment<T1,T2>::operator+(const knowledge_commitment<T1,T2> &other) const
{
return knowledge_commitment<T1,T2>(this->g + other.g,
this->h + other.h);
}
template<typename T1, typename T2>
bool knowledge_commitment<T1,T2>::is_zero() const
{
return (g.is_zero() && h.is_zero());
}
template<typename T1, typename T2>
bool knowledge_commitment<T1,T2>::operator==(const knowledge_commitment<T1,T2> &other) const
{
return (this->g == other.g &&
this->h == other.h);
}
template<typename T1, typename T2>
bool knowledge_commitment<T1,T2>::operator!=(const knowledge_commitment<T1,T2> &other) const
{
return !((*this) == other);
}
template<typename T1, typename T2, mp_size_t m>
knowledge_commitment<T1,T2> operator*(const bigint<m> &lhs, const knowledge_commitment<T1,T2> &rhs)
{
return knowledge_commitment<T1,T2>(lhs * rhs.g,
lhs * rhs.h);
}
template<typename T1, typename T2, mp_size_t m, const bigint<m> &modulus_p>
knowledge_commitment<T1,T2> operator*(const Fp_model<m, modulus_p> &lhs, const knowledge_commitment<T1,T2> &rhs)
{
return (lhs.as_bigint()) * rhs;
}
template<typename T1, typename T2>
void knowledge_commitment<T1,T2>::print() const
{
printf("knowledge_commitment.g:\n");
g.print();
printf("knowledge_commitment.h:\n");
h.print();
}
template<typename T1, typename T2>
size_t knowledge_commitment<T1,T2>::size_in_bits()
{
return T1::size_in_bits() + T2::size_in_bits();
}
template<typename T1,typename T2>
std::ostream& operator<<(std::ostream& out, const knowledge_commitment<T1,T2> &kc)
{
out << kc.g << OUTPUT_SEPARATOR << kc.h;
return out;
}
template<typename T1,typename T2>
std::istream& operator>>(std::istream& in, knowledge_commitment<T1,T2> &kc)
{
in >> kc.g;
consume_OUTPUT_SEPARATOR(in);
in >> kc.h;
return in;
}
} // libsnark
#endif // KNOWLEDGE_COMMITMENT_TCC_

View File

@@ -0,0 +1,55 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef KC_MULTIEXP_HPP_
#define KC_MULTIEXP_HPP_
/*
Split out from multiexp to prevent cyclical
dependencies. I.e. previously multiexp dependend on
knowledge_commitment, which dependend on sparse_vector, which
dependend on multiexp (to do accumulate).
Will probably go away in more general exp refactoring.
*/
#include "algebra/knowledge_commitment/knowledge_commitment.hpp"
namespace libsnark {
template<typename T1, typename T2, mp_size_t n>
knowledge_commitment<T1,T2> opt_window_wnaf_exp(const knowledge_commitment<T1,T2> &base,
const bigint<n> &scalar, const size_t scalar_bits);
template<typename T1, typename T2, typename FieldT>
knowledge_commitment<T1, T2> kc_multi_exp_with_mixed_addition(const knowledge_commitment_vector<T1, T2> &vec,
const size_t min_idx,
const size_t max_idx,
typename std::vector<FieldT>::const_iterator scalar_start,
typename std::vector<FieldT>::const_iterator scalar_end,
const size_t chunks,
const bool use_multiexp=false);
template<typename T1, typename T2>
void kc_batch_to_special(std::vector<knowledge_commitment<T1, T2> > &vec);
template<typename T1, typename T2, typename FieldT>
knowledge_commitment_vector<T1, T2> kc_batch_exp(const size_t scalar_size,
const size_t T1_window,
const size_t T2_window,
const window_table<T1> &T1_table,
const window_table<T2> &T2_table,
const FieldT &T1_coeff,
const FieldT &T2_coeff,
const std::vector<FieldT> &v,
const size_t suggested_num_chunks);
} // libsnark
#include "algebra/scalar_multiplication/kc_multiexp.tcc"
#endif // KC_MULTIEXP_HPP_

View File

@@ -0,0 +1,274 @@
/** @file
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef KC_MULTIEXP_TCC_
#define KC_MULTIEXP_TCC_
namespace libsnark {
template<typename T1, typename T2, mp_size_t n>
knowledge_commitment<T1,T2> opt_window_wnaf_exp(const knowledge_commitment<T1,T2> &base,
const bigint<n> &scalar, const size_t scalar_bits)
{
return knowledge_commitment<T1,T2>(opt_window_wnaf_exp(base.g, scalar, scalar_bits),
opt_window_wnaf_exp(base.h, scalar, scalar_bits));
}
template<typename T1, typename T2, typename FieldT>
knowledge_commitment<T1, T2> kc_multi_exp_with_mixed_addition(const knowledge_commitment_vector<T1, T2> &vec,
const size_t min_idx,
const size_t max_idx,
typename std::vector<FieldT>::const_iterator scalar_start,
typename std::vector<FieldT>::const_iterator scalar_end,
const size_t chunks,
const bool use_multiexp)
{
enter_block("Process scalar vector");
auto index_it = std::lower_bound(vec.indices.begin(), vec.indices.end(), min_idx);
const size_t offset = index_it - vec.indices.begin();
auto value_it = vec.values.begin() + offset;
const FieldT zero = FieldT::zero();
const FieldT one = FieldT::one();
std::vector<FieldT> p;
std::vector<knowledge_commitment<T1, T2> > g;
knowledge_commitment<T1, T2> acc = knowledge_commitment<T1, T2>::zero();
size_t num_skip = 0;
size_t num_add = 0;
size_t num_other = 0;
const size_t scalar_length = std::distance(scalar_start, scalar_end);
while (index_it != vec.indices.end() && *index_it < max_idx)
{
const size_t scalar_position = (*index_it) - min_idx;
assert(scalar_position < scalar_length);
const FieldT scalar = *(scalar_start + scalar_position);
if (scalar == zero)
{
// do nothing
++num_skip;
}
else if (scalar == one)
{
#ifdef USE_MIXED_ADDITION
acc.g = acc.g.mixed_add(value_it->g);
acc.h = acc.h.mixed_add(value_it->h);
#else
acc.g = acc.g + value_it->g;
acc.h = acc.h + value_it->h;
#endif
++num_add;
}
else
{
p.emplace_back(scalar);
g.emplace_back(*value_it);
++num_other;
}
++index_it;
++value_it;
}
//print_indent(); printf("* Elements of w skipped: %zu (%0.2f%%)\n", num_skip, 100.*num_skip/(num_skip+num_add+num_other));
//print_indent(); printf("* Elements of w processed with special addition: %zu (%0.2f%%)\n", num_add, 100.*num_add/(num_skip+num_add+num_other));
//print_indent(); printf("* Elements of w remaining: %zu (%0.2f%%)\n", num_other, 100.*num_other/(num_skip+num_add+num_other));
leave_block("Process scalar vector");
return acc + multi_exp<knowledge_commitment<T1, T2>, FieldT>(g.begin(), g.end(), p.begin(), p.end(), chunks, use_multiexp);
}
template<typename T1, typename T2>
void kc_batch_to_special(std::vector<knowledge_commitment<T1, T2> > &vec)
{
enter_block("Batch-convert knowledge-commitments to special form");
std::vector<T1> g_vec;
g_vec.reserve(vec.size());
for (size_t i = 0; i < vec.size(); ++i)
{
if (!vec[i].g.is_zero())
{
g_vec.emplace_back(vec[i].g);
}
}
batch_to_special_all_non_zeros<T1>(g_vec);
auto g_it = g_vec.begin();
T1 T1_zero_special = T1::zero();
T1_zero_special.to_special();
for (size_t i = 0; i < vec.size(); ++i)
{
if (!vec[i].g.is_zero())
{
vec[i].g = *g_it;
++g_it;
}
else
{
vec[i].g = T1_zero_special;
}
}
g_vec.clear();
std::vector<T2> h_vec;
h_vec.reserve(vec.size());
for (size_t i = 0; i < vec.size(); ++i)
{
if (!vec[i].h.is_zero())
{
h_vec.emplace_back(vec[i].h);
}
}
batch_to_special_all_non_zeros<T2>(h_vec);
auto h_it = h_vec.begin();
T2 T2_zero_special = T2::zero();
T2_zero_special.to_special();
for (size_t i = 0; i < vec.size(); ++i)
{
if (!vec[i].h.is_zero())
{
vec[i].h = *h_it;
++h_it;
}
else
{
vec[i].h = T2_zero_special;
}
}
g_vec.clear();
leave_block("Batch-convert knowledge-commitments to special form");
}
template<typename T1, typename T2, typename FieldT>
knowledge_commitment_vector<T1, T2> kc_batch_exp_internal(const size_t scalar_size,
const size_t T1_window,
const size_t T2_window,
const window_table<T1> &T1_table,
const window_table<T2> &T2_table,
const FieldT &T1_coeff,
const FieldT &T2_coeff,
const std::vector<FieldT> &v,
const size_t start_pos,
const size_t end_pos,
const size_t expected_size)
{
knowledge_commitment_vector<T1, T2> res;
res.values.reserve(expected_size);
res.indices.reserve(expected_size);
for (size_t pos = start_pos; pos != end_pos; ++pos)
{
if (!v[pos].is_zero())
{
res.values.emplace_back(knowledge_commitment<T1, T2>(windowed_exp(scalar_size, T1_window, T1_table, T1_coeff * v[pos]),
windowed_exp(scalar_size, T2_window, T2_table, T2_coeff * v[pos])));
res.indices.emplace_back(pos);
}
}
return res;
}
template<typename T1, typename T2, typename FieldT>
knowledge_commitment_vector<T1, T2> kc_batch_exp(const size_t scalar_size,
const size_t T1_window,
const size_t T2_window,
const window_table<T1> &T1_table,
const window_table<T2> &T2_table,
const FieldT &T1_coeff,
const FieldT &T2_coeff,
const std::vector<FieldT> &v,
const size_t suggested_num_chunks)
{
knowledge_commitment_vector<T1, T2> res;
res.domain_size_ = v.size();
size_t nonzero = 0;
for (size_t i = 0; i < v.size(); ++i)
{
nonzero += (v[i].is_zero() ? 0 : 1);
}
const size_t num_chunks = std::max((size_t)1, std::min(nonzero, suggested_num_chunks));
if (!inhibit_profiling_info)
{
print_indent(); printf("Non-zero coordinate count: %zu/%zu (%0.2f%%)\n", nonzero, v.size(), 100.*nonzero/v.size());
}
std::vector<knowledge_commitment_vector<T1, T2> > tmp(num_chunks);
std::vector<size_t> chunk_pos(num_chunks+1);
const size_t chunk_size = nonzero / num_chunks;
const size_t last_chunk = nonzero - chunk_size * (num_chunks - 1);
chunk_pos[0] = 0;
size_t cnt = 0;
size_t chunkno = 1;
for (size_t i = 0; i < v.size(); ++i)
{
cnt += (v[i].is_zero() ? 0 : 1);
if (cnt == chunk_size && chunkno < num_chunks)
{
chunk_pos[chunkno] = i;
cnt = 0;
++chunkno;
}
}
chunk_pos[num_chunks] = v.size();
#ifdef MULTICORE
#pragma omp parallel for
#endif
for (size_t i = 0; i < num_chunks; ++i)
{
tmp[i] = kc_batch_exp_internal<T1, T2, FieldT>(scalar_size, T1_window, T2_window, T1_table, T2_table, T1_coeff, T2_coeff, v,
chunk_pos[i], chunk_pos[i+1], i == num_chunks - 1 ? last_chunk : chunk_size);
#ifdef USE_MIXED_ADDITION
kc_batch_to_special<T1, T2>(tmp[i].values);
#endif
}
if (num_chunks == 1)
{
tmp[0].domain_size_ = v.size();
return tmp[0];
}
else
{
for (size_t i = 0; i < num_chunks; ++i)
{
res.values.insert(res.values.end(), tmp[i].values.begin(), tmp[i].values.end());
res.indices.insert(res.indices.end(), tmp[i].indices.begin(), tmp[i].indices.end());
}
return res;
}
}
} // libsnark
#endif // KC_MULTIEXP_TCC_

View File

@@ -0,0 +1,110 @@
/** @file
*****************************************************************************
Declaration of interfaces for multi-exponentiation routines.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef MULTIEXP_HPP_
#define MULTIEXP_HPP_
namespace libsnark {
/**
* Naive multi-exponentiation individually multiplies each base by the
* corresponding scalar and adds up the results.
*/
template<typename T, typename FieldT>
T naive_exp(typename std::vector<T>::const_iterator vec_start,
typename std::vector<T>::const_iterator vec_end,
typename std::vector<FieldT>::const_iterator scalar_start,
typename std::vector<FieldT>::const_iterator scalar_end);
template<typename T, typename FieldT>
T naive_plain_exp(typename std::vector<T>::const_iterator vec_start,
typename std::vector<T>::const_iterator vec_end,
typename std::vector<FieldT>::const_iterator scalar_start,
typename std::vector<FieldT>::const_iterator scalar_end);
/**
* Naive multi-exponentiation uses a variant of the Bos-Coster algorithm [1],
* and implementation suggestions from [2].
*
* [1] = Bos and Coster, "Addition chain heuristics", CRYPTO '89
* [2] = Bernstein, Duif, Lange, Schwabe, and Yang, "High-speed high-security signatures", CHES '11
*/
template<typename T, typename FieldT>
T multi_exp(typename std::vector<T>::const_iterator vec_start,
typename std::vector<T>::const_iterator vec_end,
typename std::vector<FieldT>::const_iterator scalar_start,
typename std::vector<FieldT>::const_iterator scalar_end,
const size_t chunks,
const bool use_multiexp=false);
/**
* A variant of multi_exp that takes advantage of the method mixed_add (instead of the operator '+').
*/
template<typename T, typename FieldT>
T multi_exp_with_mixed_addition(typename std::vector<T>::const_iterator vec_start,
typename std::vector<T>::const_iterator vec_end,
typename std::vector<FieldT>::const_iterator scalar_start,
typename std::vector<FieldT>::const_iterator scalar_end,
const size_t chunks,
const bool use_multiexp);
/**
* A window table stores window sizes for different instance sizes for fixed-base multi-scalar multiplications.
*/
template<typename T>
using window_table = std::vector<std::vector<T> >;
/**
* Compute window size for the given number of scalars.
*/
template<typename T>
size_t get_exp_window_size(const size_t num_scalars);
/**
* Compute table of window sizes.
*/
template<typename T>
window_table<T> get_window_table(const size_t scalar_size,
const size_t window,
const T &g);
template<typename T, typename FieldT>
T windowed_exp(const size_t scalar_size,
const size_t window,
const window_table<T> &powers_of_g,
const FieldT &pow);
template<typename T, typename FieldT>
std::vector<T> batch_exp(const size_t scalar_size,
const size_t window,
const window_table<T> &table,
const std::vector<FieldT> &v);
template<typename T, typename FieldT>
std::vector<T> batch_exp_with_coeff(const size_t scalar_size,
const size_t window,
const window_table<T> &table,
const FieldT &coeff,
const std::vector<FieldT> &v);
// defined in every curve
template<typename T>
void batch_to_special_all_non_zeros(std::vector<T> &vec);
template<typename T>
void batch_to_special(std::vector<T> &vec);
} // libsnark
#include "algebra/scalar_multiplication/multiexp.tcc"
#endif // MULTIEXP_HPP_

View File

@@ -0,0 +1,590 @@
/** @file
*****************************************************************************
Implementation of interfaces for multi-exponentiation routines.
See multiexp.hpp .
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef MULTIEXP_TCC_
#define MULTIEXP_TCC_
#include "algebra/fields/fp_aux.tcc"
#include <algorithm>
#include <cassert>
#include <type_traits>
#include "common/profiling.hpp"
#include "common/utils.hpp"
#include "algebra/scalar_multiplication/wnaf.hpp"
namespace libsnark {
template<mp_size_t n>
class ordered_exponent {
// to use std::push_heap and friends later
public:
size_t idx;
bigint<n> r;
ordered_exponent(const size_t idx, const bigint<n> &r) : idx(idx), r(r) {};
bool operator<(const ordered_exponent<n> &other) const
{
#if defined(__x86_64__) && defined(USE_ASM)
if (n == 3)
{
long res;
__asm__
("// check for overflow \n\t"
"mov $0, %[res] \n\t"
ADD_CMP(16)
ADD_CMP(8)
ADD_CMP(0)
"jmp done%= \n\t"
"subtract%=: \n\t"
"mov $1, %[res] \n\t"
"done%=: \n\t"
: [res] "=&r" (res)
: [A] "r" (other.r.data), [mod] "r" (this->r.data)
: "cc", "%rax");
return res;
}
else if (n == 4)
{
long res;
__asm__
("// check for overflow \n\t"
"mov $0, %[res] \n\t"
ADD_CMP(24)
ADD_CMP(16)
ADD_CMP(8)
ADD_CMP(0)
"jmp done%= \n\t"
"subtract%=: \n\t"
"mov $1, %[res] \n\t"
"done%=: \n\t"
: [res] "=&r" (res)
: [A] "r" (other.r.data), [mod] "r" (this->r.data)
: "cc", "%rax");
return res;
}
else if (n == 5)
{
long res;
__asm__
("// check for overflow \n\t"
"mov $0, %[res] \n\t"
ADD_CMP(32)
ADD_CMP(24)
ADD_CMP(16)
ADD_CMP(8)
ADD_CMP(0)
"jmp done%= \n\t"
"subtract%=: \n\t"
"mov $1, %[res] \n\t"
"done%=: \n\t"
: [res] "=&r" (res)
: [A] "r" (other.r.data), [mod] "r" (this->r.data)
: "cc", "%rax");
return res;
}
else
#endif
{
return (mpn_cmp(this->r.data, other.r.data, n) < 0);
}
}
};
template<typename T, typename FieldT>
T naive_exp(typename std::vector<T>::const_iterator vec_start,
typename std::vector<T>::const_iterator vec_end,
typename std::vector<FieldT>::const_iterator scalar_start,
typename std::vector<FieldT>::const_iterator scalar_end)
{
T result(T::zero());
typename std::vector<T>::const_iterator vec_it;
typename std::vector<FieldT>::const_iterator scalar_it;
for (vec_it = vec_start, scalar_it = scalar_start; vec_it != vec_end; ++vec_it, ++scalar_it)
{
bigint<FieldT::num_limbs> scalar_bigint = scalar_it->as_bigint();
result = result + opt_window_wnaf_exp(*vec_it, scalar_bigint, scalar_bigint.num_bits());
}
assert(scalar_it == scalar_end);
return result;
}
template<typename T, typename FieldT>
T naive_plain_exp(typename std::vector<T>::const_iterator vec_start,
typename std::vector<T>::const_iterator vec_end,
typename std::vector<FieldT>::const_iterator scalar_start,
typename std::vector<FieldT>::const_iterator scalar_end)
{
T result(T::zero());
typename std::vector<T>::const_iterator vec_it;
typename std::vector<FieldT>::const_iterator scalar_it;
for (vec_it = vec_start, scalar_it = scalar_start; vec_it != vec_end; ++vec_it, ++scalar_it)
{
result = result + (*scalar_it) * (*vec_it);
}
assert(scalar_it == scalar_end);
return result;
}
/*
The multi-exponentiation algorithm below is a variant of the Bos-Coster algorithm
[Bos and Coster, "Addition chain heuristics", CRYPTO '89].
The implementation uses suggestions from
[Bernstein, Duif, Lange, Schwabe, and Yang, "High-speed high-security signatures", CHES '11].
*/
template<typename T, typename FieldT>
T multi_exp_inner(typename std::vector<T>::const_iterator vec_start,
typename std::vector<T>::const_iterator vec_end,
typename std::vector<FieldT>::const_iterator scalar_start,
typename std::vector<FieldT>::const_iterator scalar_end)
{
const mp_size_t n = std::remove_reference<decltype(*scalar_start)>::type::num_limbs;
if (vec_start == vec_end)
{
return T::zero();
}
if (vec_start + 1 == vec_end)
{
return (*scalar_start)*(*vec_start);
}
std::vector<ordered_exponent<n> > opt_q;
const size_t vec_len = scalar_end - scalar_start;
const size_t odd_vec_len = (vec_len % 2 == 1 ? vec_len : vec_len + 1);
opt_q.reserve(odd_vec_len);
std::vector<T> g;
g.reserve(odd_vec_len);
typename std::vector<T>::const_iterator vec_it;
typename std::vector<FieldT>::const_iterator scalar_it;
size_t i;
for (i=0, vec_it = vec_start, scalar_it = scalar_start; vec_it != vec_end; ++vec_it, ++scalar_it, ++i)
{
g.emplace_back(*vec_it);
opt_q.emplace_back(ordered_exponent<n>(i, scalar_it->as_bigint()));
}
std::make_heap(opt_q.begin(),opt_q.end());
assert(scalar_it == scalar_end);
if (vec_len != odd_vec_len)
{
g.emplace_back(T::zero());
opt_q.emplace_back(ordered_exponent<n>(odd_vec_len - 1, bigint<n>(0ul)));
}
assert(g.size() % 2 == 1);
assert(opt_q.size() == g.size());
T opt_result = T::zero();
while (true)
{
ordered_exponent<n> &a = opt_q[0];
ordered_exponent<n> &b = (opt_q[1] < opt_q[2] ? opt_q[2] : opt_q[1]);
const size_t abits = a.r.num_bits();
if (b.r.is_zero())
{
// opt_result = opt_result + (a.r * g[a.idx]);
opt_result = opt_result + opt_window_wnaf_exp(g[a.idx], a.r, abits);
break;
}
const size_t bbits = b.r.num_bits();
const size_t limit = (abits-bbits >= 20 ? 20 : abits-bbits);
if (bbits < 1ul<<limit)
{
/*
In this case, exponentiating to the power of a is cheaper than
subtracting b from a multiple times, so let's do it directly
*/
// opt_result = opt_result + (a.r * g[a.idx]);
opt_result = opt_result + opt_window_wnaf_exp(g[a.idx], a.r, abits);
#ifdef DEBUG
printf("Skipping the following pair (%zu bit number vs %zu bit):\n", abits, bbits);
a.r.print();
b.r.print();
#endif
a.r.clear();
}
else
{
// x A + y B => (x-y) A + y (B+A)
mpn_sub_n(a.r.data, a.r.data, b.r.data, n);
g[b.idx] = g[b.idx] + g[a.idx];
}
// regardless of whether a was cleared or subtracted from we push it down, then take back up
/* heapify A down */
size_t a_pos = 0;
while (2*a_pos + 2< odd_vec_len)
{
// this is a max-heap so to maintain a heap property we swap with the largest of the two
if (opt_q[2*a_pos+1] < opt_q[2*a_pos+2])
{
std::swap(opt_q[a_pos], opt_q[2*a_pos+2]);
a_pos = 2*a_pos+2;
}
else
{
std::swap(opt_q[a_pos], opt_q[2*a_pos+1]);
a_pos = 2*a_pos+1;
}
}
/* now heapify A up appropriate amount of times */
while (a_pos > 0 && opt_q[(a_pos-1)/2] < opt_q[a_pos])
{
std::swap(opt_q[a_pos], opt_q[(a_pos-1)/2]);
a_pos = (a_pos-1) / 2;
}
}
return opt_result;
}
template<typename T, typename FieldT>
T multi_exp(typename std::vector<T>::const_iterator vec_start,
typename std::vector<T>::const_iterator vec_end,
typename std::vector<FieldT>::const_iterator scalar_start,
typename std::vector<FieldT>::const_iterator scalar_end,
const size_t chunks,
const bool use_multiexp)
{
const size_t total = vec_end - vec_start;
if (total < chunks)
{
return naive_exp<T, FieldT>(vec_start, vec_end, scalar_start, scalar_end);
}
const size_t one = total/chunks;
std::vector<T> partial(chunks, T::zero());
if (use_multiexp)
{
#ifdef MULTICORE
#pragma omp parallel for
#endif
for (size_t i = 0; i < chunks; ++i)
{
partial[i] = multi_exp_inner<T, FieldT>(vec_start + i*one,
(i == chunks-1 ? vec_end : vec_start + (i+1)*one),
scalar_start + i*one,
(i == chunks-1 ? scalar_end : scalar_start + (i+1)*one));
}
}
else
{
#ifdef MULTICORE
#pragma omp parallel for
#endif
for (size_t i = 0; i < chunks; ++i)
{
partial[i] = naive_exp<T, FieldT>(vec_start + i*one,
(i == chunks-1 ? vec_end : vec_start + (i+1)*one),
scalar_start + i*one,
(i == chunks-1 ? scalar_end : scalar_start + (i+1)*one));
}
}
T final = T::zero();
for (size_t i = 0; i < chunks; ++i)
{
final = final + partial[i];
}
return final;
}
template<typename T, typename FieldT>
T multi_exp_with_mixed_addition(typename std::vector<T>::const_iterator vec_start,
typename std::vector<T>::const_iterator vec_end,
typename std::vector<FieldT>::const_iterator scalar_start,
typename std::vector<FieldT>::const_iterator scalar_end,
const size_t chunks,
const bool use_multiexp)
{
assert(std::distance(vec_start, vec_end) == std::distance(scalar_start, scalar_end));
enter_block("Process scalar vector");
auto value_it = vec_start;
auto scalar_it = scalar_start;
const FieldT zero = FieldT::zero();
const FieldT one = FieldT::one();
std::vector<FieldT> p;
std::vector<T> g;
T acc = T::zero();
size_t num_skip = 0;
size_t num_add = 0;
size_t num_other = 0;
for (; scalar_it != scalar_end; ++scalar_it, ++value_it)
{
if (*scalar_it == zero)
{
// do nothing
++num_skip;
}
else if (*scalar_it == one)
{
#ifdef USE_MIXED_ADDITION
acc = acc.mixed_add(*value_it);
#else
acc = acc + (*value_it);
#endif
++num_add;
}
else
{
p.emplace_back(*scalar_it);
g.emplace_back(*value_it);
++num_other;
}
}
//print_indent(); printf("* Elements of w skipped: %zu (%0.2f%%)\n", num_skip, 100.*num_skip/(num_skip+num_add+num_other));
//print_indent(); printf("* Elements of w processed with special addition: %zu (%0.2f%%)\n", num_add, 100.*num_add/(num_skip+num_add+num_other));
//print_indent(); printf("* Elements of w remaining: %zu (%0.2f%%)\n", num_other, 100.*num_other/(num_skip+num_add+num_other));
leave_block("Process scalar vector");
return acc + multi_exp<T, FieldT>(g.begin(), g.end(), p.begin(), p.end(), chunks, use_multiexp);
}
template<typename T>
size_t get_exp_window_size(const size_t num_scalars)
{
if (T::fixed_base_exp_window_table.empty())
{
#ifdef LOWMEM
return 14;
#else
return 17;
#endif
}
size_t window = 1;
for (long i = T::fixed_base_exp_window_table.size()-1; i >= 0; --i)
{
#ifdef DEBUG
if (!inhibit_profiling_info)
{
printf("%ld %zu %zu\n", i, num_scalars, T::fixed_base_exp_window_table[i]);
}
#endif
if (T::fixed_base_exp_window_table[i] != 0 && num_scalars >= T::fixed_base_exp_window_table[i])
{
window = i+1;
break;
}
}
if (!inhibit_profiling_info)
{
print_indent(); printf("Choosing window size %zu for %zu elements\n", window, num_scalars);
}
#ifdef LOWMEM
window = std::min((size_t)14, window);
#endif
return window;
}
template<typename T>
window_table<T> get_window_table(const size_t scalar_size,
const size_t window,
const T &g)
{
const size_t in_window = 1ul<<window;
const size_t outerc = (scalar_size+window-1)/window;
const size_t last_in_window = 1ul<<(scalar_size - (outerc-1)*window);
#ifdef DEBUG
if (!inhibit_profiling_info)
{
print_indent(); printf("* scalar_size=%zu; window=%zu; in_window=%zu; outerc=%zu\n", scalar_size, window, in_window, outerc);
}
#endif
window_table<T> powers_of_g(outerc, std::vector<T>(in_window, T::zero()));
T gouter = g;
for (size_t outer = 0; outer < outerc; ++outer)
{
T ginner = T::zero();
size_t cur_in_window = outer == outerc-1 ? last_in_window : in_window;
for (size_t inner = 0; inner < cur_in_window; ++inner)
{
powers_of_g[outer][inner] = ginner;
ginner = ginner + gouter;
}
for (size_t i = 0; i < window; ++i)
{
gouter = gouter + gouter;
}
}
return powers_of_g;
}
template<typename T, typename FieldT>
T windowed_exp(const size_t scalar_size,
const size_t window,
const window_table<T> &powers_of_g,
const FieldT &pow)
{
const size_t outerc = (scalar_size+window-1)/window;
const bigint<FieldT::num_limbs> pow_val = pow.as_bigint();
/* exp */
T res = powers_of_g[0][0];
for (size_t outer = 0; outer < outerc; ++outer)
{
size_t inner = 0;
for (size_t i = 0; i < window; ++i)
{
if (pow_val.test_bit(outer*window + i))
{
inner |= 1u << i;
}
}
res = res + powers_of_g[outer][inner];
}
return res;
}
template<typename T, typename FieldT>
std::vector<T> batch_exp(const size_t scalar_size,
const size_t window,
const window_table<T> &table,
const std::vector<FieldT> &v)
{
if (!inhibit_profiling_info)
{
print_indent();
}
std::vector<T> res(v.size(), table[0][0]);
#ifdef MULTICORE
#pragma omp parallel for
#endif
for (size_t i = 0; i < v.size(); ++i)
{
res[i] = windowed_exp(scalar_size, window, table, v[i]);
if (!inhibit_profiling_info && (i % 10000 == 0))
{
printf(".");
fflush(stdout);
}
}
if (!inhibit_profiling_info)
{
printf(" DONE!\n");
}
return res;
}
template<typename T, typename FieldT>
std::vector<T> batch_exp_with_coeff(const size_t scalar_size,
const size_t window,
const window_table<T> &table,
const FieldT &coeff,
const std::vector<FieldT> &v)
{
if (!inhibit_profiling_info)
{
print_indent();
}
std::vector<T> res(v.size(), table[0][0]);
#ifdef MULTICORE
#pragma omp parallel for
#endif
for (size_t i = 0; i < v.size(); ++i)
{
res[i] = windowed_exp(scalar_size, window, table, coeff * v[i]);
if (!inhibit_profiling_info && (i % 10000 == 0))
{
printf(".");
fflush(stdout);
}
}
if (!inhibit_profiling_info)
{
printf(" DONE!\n");
}
return res;
}
template<typename T>
void batch_to_special(std::vector<T> &vec)
{
enter_block("Batch-convert elements to special form");
std::vector<T> non_zero_vec;
for (size_t i = 0; i < vec.size(); ++i)
{
if (!vec[i].is_zero())
{
non_zero_vec.emplace_back(vec[i]);
}
}
batch_to_special_all_non_zeros<T>(non_zero_vec);
auto it = non_zero_vec.begin();
T zero_special = T::zero();
zero_special.to_special();
for (size_t i = 0; i < vec.size(); ++i)
{
if (!vec[i].is_zero())
{
vec[i] = *it;
++it;
}
else
{
vec[i] = zero_special;
}
}
leave_block("Batch-convert elements to special form");
}
} // libsnark
#endif // MULTIEXP_TCC_

View File

@@ -0,0 +1,39 @@
/** @file
*****************************************************************************
Declaration of interfaces for wNAF ("width-w Non-Adjacent Form") exponentiation routines.
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef WNAF_HPP_
#define WNAF_HPP_
namespace libsnark {
/**
* Find the wNAF representation of the given scalar relative to the given window size.
*/
template<mp_size_t n>
std::vector<long> find_wnaf(const size_t window_size, const bigint<n> &scalar);
/**
* In additive notation, use wNAF exponentiation (with the given window size) to compute scalar * base.
*/
template<typename T, mp_size_t n>
T fixed_window_wnaf_exp(const size_t window_size, const T &base, const bigint<n> &scalar);
/**
* In additive notation, use wNAF exponentiation (with the window size determined by T) to compute scalar * base.
*/
template<typename T, mp_size_t n>
T opt_window_wnaf_exp(const T &base, const bigint<n> &scalar, const size_t scalar_bits);
} // libsnark
#include "algebra/scalar_multiplication/wnaf.tcc"
#endif // WNAF_HPP_

View File

@@ -0,0 +1,123 @@
/** @file
*****************************************************************************
Implementation of interfaces for wNAF ("weighted Non-Adjacent Form") exponentiation routines.
See wnaf.hpp .
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef WNAF_TCC_
#define WNAF_TCC_
namespace libsnark {
template<mp_size_t n>
std::vector<long> find_wnaf(const size_t window_size, const bigint<n> &scalar)
{
const size_t length = scalar.max_bits(); // upper bound
std::vector<long> res(length+1);
bigint<n> c = scalar;
long j = 0;
while (!c.is_zero())
{
long u;
if ((c.data[0] & 1) == 1)
{
u = c.data[0] % (1u << (window_size+1));
if (u > (1 << window_size))
{
u = u - (1 << (window_size+1));
}
if (u > 0)
{
mpn_sub_1(c.data, c.data, n, u);
}
else
{
mpn_add_1(c.data, c.data, n, -u);
}
}
else
{
u = 0;
}
res[j] = u;
++j;
mpn_rshift(c.data, c.data, n, 1); // c = c/2
}
return res;
}
template<typename T, mp_size_t n>
T fixed_window_wnaf_exp(const size_t window_size, const T &base, const bigint<n> &scalar)
{
std::vector<long> naf = find_wnaf(window_size, scalar);
std::vector<T> table(1ul<<(window_size-1));
T tmp = base;
T dbl = base.dbl();
for (size_t i = 0; i < 1ul<<(window_size-1); ++i)
{
table[i] = tmp;
tmp = tmp + dbl;
}
T res = T::zero();
bool found_nonzero = false;
for (long i = naf.size()-1; i >= 0; --i)
{
if (found_nonzero)
{
res = res.dbl();
}
if (naf[i] != 0)
{
found_nonzero = true;
if (naf[i] > 0)
{
res = res + table[naf[i]/2];
}
else
{
res = res - table[(-naf[i])/2];
}
}
}
return res;
}
template<typename T, mp_size_t n>
T opt_window_wnaf_exp(const T &base, const bigint<n> &scalar, const size_t scalar_bits)
{
size_t best = 0;
for (long i = T::wnaf_window_table.size() - 1; i >= 0; --i)
{
if (scalar_bits >= T::wnaf_window_table[i])
{
best = i+1;
break;
}
}
if (best > 0)
{
return fixed_window_wnaf_exp(best, base, scalar);
}
else
{
return scalar * base;
}
}
} // libsnark
#endif // WNAF_TCC_