Squashed 'src/snark/' content from commit 9ada3f8

git-subtree-dir: src/snark
git-subtree-split: 9ada3f84ab484c57b2247c2f41091fd6a0916573
This commit is contained in:
Jack Grigg
2017-08-02 11:17:25 +01:00
commit 51e448641d
123 changed files with 22264 additions and 0 deletions

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, n);
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_

182
src/algebra/fields/fp.hpp Normal file
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_

790
src/algebra/fields/fp.tcc Normal file
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_

120
src/algebra/fields/fp2.hpp Normal file
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_

261
src/algebra/fields/fp2.tcc Normal file
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_

122
src/algebra/fields/fp3.hpp Normal file
View File

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

259
src/algebra/fields/fp3.tcc Normal file
View File

@@ -0,0 +1,259 @@
/** @file
*****************************************************************************
Implementation of arithmetic in the finite field F[p^3].
*****************************************************************************
* @author This file is part of libsnark, developed by SCIPR Lab
* and contributors (see AUTHORS).
* @copyright MIT license (see LICENSE file)
*****************************************************************************/
#ifndef FP3_TCC_
#define FP3_TCC_
#include "algebra/fields/field_utils.hpp"
namespace libsnark {
template<mp_size_t n, const bigint<n>& modulus>
Fp3_model<n,modulus> Fp3_model<n,modulus>::zero()
{
return Fp3_model<n, modulus>(my_Fp::zero(), my_Fp::zero(), my_Fp::zero());
}
template<mp_size_t n, const bigint<n>& modulus>
Fp3_model<n,modulus> Fp3_model<n,modulus>::one()
{
return Fp3_model<n, modulus>(my_Fp::one(), my_Fp::zero(), my_Fp::zero());
}
template<mp_size_t n, const bigint<n>& modulus>
Fp3_model<n,modulus> Fp3_model<n,modulus>::random_element()
{
Fp3_model<n, modulus> r;
r.c0 = my_Fp::random_element();
r.c1 = my_Fp::random_element();
r.c2 = my_Fp::random_element();
return r;
}
template<mp_size_t n, const bigint<n>& modulus>
bool Fp3_model<n,modulus>::operator==(const Fp3_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 Fp3_model<n,modulus>::operator!=(const Fp3_model<n,modulus> &other) const
{
return !(operator==(other));
}
template<mp_size_t n, const bigint<n>& modulus>
Fp3_model<n,modulus> Fp3_model<n,modulus>::operator+(const Fp3_model<n,modulus> &other) const
{
return Fp3_model<n,modulus>(this->c0 + other.c0,
this->c1 + other.c1,
this->c2 + other.c2);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp3_model<n,modulus> Fp3_model<n,modulus>::operator-(const Fp3_model<n,modulus> &other) const
{
return Fp3_model<n,modulus>(this->c0 - other.c0,
this->c1 - other.c1,
this->c2 - other.c2);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp3_model<n, modulus> operator*(const Fp_model<n, modulus> &lhs, const Fp3_model<n, modulus> &rhs)
{
return Fp3_model<n,modulus>(lhs*rhs.c0,
lhs*rhs.c1,
lhs*rhs.c2);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp3_model<n,modulus> Fp3_model<n,modulus>::operator*(const Fp3_model<n,modulus> &other) const
{
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 4 (Karatsuba) */
const my_Fp
&A = other.c0, &B = other.c1, &C = other.c2,
&a = this->c0, &b = this->c1, &c = this->c2;
const my_Fp aA = a*A;
const my_Fp bB = b*B;
const my_Fp cC = c*C;
return Fp3_model<n,modulus>(aA + non_residue*((b+c)*(B+C)-bB-cC),
(a+b)*(A+B)-aA-bB+non_residue*cC,
(a+c)*(A+C)-aA+bB-cC);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp3_model<n,modulus> Fp3_model<n,modulus>::operator-() const
{
return Fp3_model<n,modulus>(-this->c0,
-this->c1,
-this->c2);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp3_model<n,modulus> Fp3_model<n,modulus>::squared() const
{
/* Devegili OhEig Scott Dahab --- Multiplication and Squaring on Pairing-Friendly Fields.pdf; Section 4 (CH-SQR2) */
const my_Fp
&a = this->c0, &b = this->c1, &c = this->c2;
const my_Fp s0 = a.squared();
const my_Fp ab = a*b;
const my_Fp s1 = ab + ab;
const my_Fp s2 = (a - b + c).squared();
const my_Fp bc = b*c;
const my_Fp s3 = bc + bc;
const my_Fp s4 = c.squared();
return Fp3_model<n,modulus>(s0 + non_residue * s3,
s1 + non_residue * s4,
s1 + s2 + s3 - s0 - s4);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp3_model<n,modulus> Fp3_model<n,modulus>::inverse() const
{
const my_Fp
&a = this->c0, &b = this->c1, &c = this->c2;
/* From "High-Speed Software Implementation of the Optimal Ate Pairing over Barreto-Naehrig Curves"; Algorithm 17 */
const my_Fp t0 = a.squared();
const my_Fp t1 = b.squared();
const my_Fp t2 = c.squared();
const my_Fp t3 = a*b;
const my_Fp t4 = a*c;
const my_Fp t5 = b*c;
const my_Fp c0 = t0 - non_residue * t5;
const my_Fp c1 = non_residue * t2 - t3;
const my_Fp c2 = t1 - t4; // typo in paper referenced above. should be "-" as per Scott, but is "*"
const my_Fp t6 = (a * c0 + non_residue * (c * c1 + b * c2)).inverse();
return Fp3_model<n,modulus>(t6 * c0, t6 * c1, t6 * c2);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp3_model<n,modulus> Fp3_model<n,modulus>::Frobenius_map(unsigned long power) const
{
return Fp3_model<n,modulus>(c0,
Frobenius_coeffs_c1[power % 3] * c1,
Frobenius_coeffs_c2[power % 3] * c2);
}
template<mp_size_t n, const bigint<n>& modulus>
Fp3_model<n,modulus> Fp3_model<n,modulus>::sqrt() const
{
Fp3_model<n,modulus> one = Fp3_model<n,modulus>::one();
size_t v = Fp3_model<n,modulus>::s;
Fp3_model<n,modulus> z = Fp3_model<n,modulus>::nqr_to_t;
Fp3_model<n,modulus> w = (*this)^Fp3_model<n,modulus>::t_minus_1_over_2;
Fp3_model<n,modulus> x = (*this) * w;
Fp3_model<n,modulus> b = x * w; // b = (*this)^t
#if DEBUG
// check if square with euler's criterion
Fp3_model<n,modulus> check = b;
for (size_t i = 0; i < v-1; ++i)
{
check = check.squared();
}
if (check != one)
{
assert(0);
}
#endif
// compute square root with Tonelli--Shanks
// (does not terminate if not a square!)
while (b != one)
{
size_t m = 0;
Fp3_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>
Fp3_model<n,modulus> Fp3_model<n,modulus>::operator^(const bigint<m> &pow) const
{
return power<Fp3_model<n, modulus> >(*this, pow);
}
template<mp_size_t n, const bigint<n>& modulus>
std::ostream& operator<<(std::ostream &out, const Fp3_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, Fp3_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<Fp3_model<n, modulus> > &v)
{
out << v.size() << "\n";
for (const Fp3_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<Fp3_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)
{
Fp3_model<n, modulus> el;
in >> el;
v.emplace_back(el);
}
return in;
}
} // libsnark
#endif // FP3_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,107 @@
/**
*****************************************************************************
* @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"
using namespace libsnark;
void test_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);
assert(b0.as_ulong() == 0ul);
assert(b0.is_zero());
assert(b1.as_ulong() == 76749407ul);
assert(!(b1.is_zero()));
assert(b2.as_ulong() == 15747124762497195938ul);
assert(!(b2.is_zero()));
assert(b0 != b1);
assert(!(b0 == b1));
assert(b2.max_bits() == 128);
assert(b2.num_bits() == 99);
for (size_t i = 0; i < 128; i++) {
assert(b2.test_bit(i) == (b2_binary[127-i] == '1'));
}
bigint<3> b3 = b2 * b1;
assert(b3 == bigint<3>(b3_decimal));
assert(!(b3.is_zero()));
bigint<3> b3a { b3 };
assert(b3a == bigint<3>(b3_decimal));
assert(b3a == b3);
assert(!(b3a.is_zero()));
mpz_t m3;
mpz_init(m3);
b3.to_mpz(m3);
bigint<3> b3b { m3 };
assert(b3b == b3);
bigint<2> quotient;
bigint<2> remainder;
bigint<3>::div_qr(quotient, remainder, b3, b2);
assert(quotient.num_bits() < GMP_NUMB_BITS);
assert(quotient.as_ulong() == b1.as_ulong());
bigint<1> b1inc = bigint<1>("76749408");
bigint<1> b1a = quotient.shorten(b1inc, "test");
assert(b1a == b1);
assert(remainder.is_zero());
remainder.limit(b2, "test");
try {
(void)(quotient.shorten(b1, "test"));
assert(false);
} catch (std::domain_error) {}
try {
remainder.limit(remainder, "test");
assert(false);
} catch (std::domain_error) {}
bigint<1> br = bigint<1>("42");
b3 += br;
assert(b3 != b3a);
assert(b3 > b3a);
assert(!(b3a > b3));
bigint<3>::div_qr(quotient, remainder, b3, b2);
assert(quotient.num_bits() < GMP_NUMB_BITS);
assert(quotient.as_ulong() == b1.as_ulong());
assert(remainder.num_bits() < GMP_NUMB_BITS);
assert(remainder.as_ulong() == 42);
b3a.clear();
assert(b3a.is_zero());
assert(b3a.num_bits() == 0);
assert(!(b3.is_zero()));
bigint<4> bx = bigint<4>().randomize();
bigint<4> by = bigint<4>().randomize();
assert(!(bx == by));
// TODO: test serialization
}
int main(void)
{
test_bigint();
return 0;
}

View File

@@ -0,0 +1,245 @@
/**
*****************************************************************************
* @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"
#include "algebra/curves/edwards/edwards_pp.hpp"
#include "algebra/curves/mnt/mnt4/mnt4_pp.hpp"
#include "algebra/curves/mnt/mnt6/mnt6_pp.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"
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);
assert(a_ser == a);
FieldT b = FieldT::random_element();
FieldT c = FieldT::random_element();
FieldT d = FieldT::random_element();
assert(a != zero);
assert(a != one);
assert(a * a == a.squared());
assert((a + b).squared() == a.squared() + a*b + b*a + b.squared());
assert((a + b)*(c + d) == a*c + a*d + b*c + b*d);
assert(a - b == a + (-b));
assert(a - b == (-b) + a);
assert((a ^ rand1) * (a ^ rand2) == (a^randsum));
assert(a * a.inverse() == one);
assert((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();
assert(asq.sqrt() == a || asq.sqrt() == -a);
}
}
template<typename FieldT>
void test_two_squarings()
{
FieldT a = FieldT::random_element();
assert(a.squared() == a * a);
assert(a.squared() == a.squared_complex());
assert(a.squared() == a.squared_karatsuba());
}
template<typename FieldT>
void test_Frobenius()
{
FieldT a = FieldT::random_element();
assert(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);
assert(a_qi == a_q);
a_q = a_q ^ FieldT::base_field_char();
}
}
template<typename FieldT>
void test_unitary_inverse()
{
assert(FieldT::extension_degree() % 2 == 0);
FieldT a = FieldT::random_element();
FieldT aqcubed_minus1 = a.Frobenius_map(FieldT::extension_degree()/2) * a.inverse();
assert(aqcubed_minus1.inverse() == aqcubed_minus1.unitary_inverse());
}
template<typename FieldT>
void test_cyclotomic_squaring();
template<>
void test_cyclotomic_squaring<Fqk<edwards_pp> >()
{
typedef Fqk<edwards_pp> FieldT;
assert(FieldT::extension_degree() % 2 == 0);
FieldT a = FieldT::random_element();
FieldT a_unitary = a.Frobenius_map(FieldT::extension_degree()/2) * a.inverse();
// beta = a^((q^(k/2)-1)*(q+1))
FieldT beta = a_unitary.Frobenius_map(1) * a_unitary;
assert(beta.cyclotomic_squared() == beta.squared());
}
template<>
void test_cyclotomic_squaring<Fqk<mnt4_pp> >()
{
typedef Fqk<mnt4_pp> FieldT;
assert(FieldT::extension_degree() % 2 == 0);
FieldT a = FieldT::random_element();
FieldT a_unitary = a.Frobenius_map(FieldT::extension_degree()/2) * a.inverse();
// beta = a^(q^(k/2)-1)
FieldT beta = a_unitary;
assert(beta.cyclotomic_squared() == beta.squared());
}
template<>
void test_cyclotomic_squaring<Fqk<mnt6_pp> >()
{
typedef Fqk<mnt6_pp> FieldT;
assert(FieldT::extension_degree() % 2 == 0);
FieldT a = FieldT::random_element();
FieldT a_unitary = a.Frobenius_map(FieldT::extension_degree()/2) * a.inverse();
// beta = a^((q^(k/2)-1)*(q+1))
FieldT beta = a_unitary.Frobenius_map(1) * a_unitary;
assert(beta.cyclotomic_squared() == beta.squared());
}
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;
assert(res == correct_res);
// {v0, v3, v4, v5}
const FieldT u = (FieldT::one() - beta).inverse();
assert(v0 == u * c0 + beta * u * c2 - beta * u * FieldT(2).inverse() * v1 - beta * u * FieldT(2).inverse() * v2 + beta * v6);
assert(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);
assert(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);
assert(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
}
}
int main(void)
{
edwards_pp::init_public_params();
test_all_fields<edwards_pp>();
test_cyclotomic_squaring<Fqk<edwards_pp> >();
mnt4_pp::init_public_params();
test_all_fields<mnt4_pp>();
test_Fp4_tom_cook<mnt4_Fq4>();
test_two_squarings<Fqe<mnt4_pp> >();
test_cyclotomic_squaring<Fqk<mnt4_pp> >();
mnt6_pp::init_public_params();
test_all_fields<mnt6_pp>();
test_cyclotomic_squaring<Fqk<mnt6_pp> >();
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
}