mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2053 lines
		
	
	
	
		
			51 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2053 lines
		
	
	
	
		
			51 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* 
 | 
						|
    This file is part of TON Blockchain source code.
 | 
						|
 | 
						|
    TON Blockchain is free software; you can redistribute it and/or
 | 
						|
    modify it under the terms of the GNU General Public License
 | 
						|
    as published by the Free Software Foundation; either version 2
 | 
						|
    of the License, or (at your option) any later version.
 | 
						|
 | 
						|
    TON Blockchain is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
    GNU General Public License for more details.
 | 
						|
 | 
						|
    You should have received a copy of the GNU General Public License
 | 
						|
    along with TON Blockchain.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 | 
						|
    In addition, as a special exception, the copyright holders give permission 
 | 
						|
    to link the code of portions of this program with the OpenSSL library. 
 | 
						|
    You must obey the GNU General Public License in all respects for all 
 | 
						|
    of the code used other than OpenSSL. If you modify file(s) with this 
 | 
						|
    exception, you may extend this exception to your version of the file(s), 
 | 
						|
    but you are not obligated to do so. If you do not wish to do so, delete this 
 | 
						|
    exception statement from your version. If you delete this exception statement 
 | 
						|
    from all source files in the program, then also delete it here.
 | 
						|
 | 
						|
    Copyright 2017-2020 Telegram Systems LLP
 | 
						|
*/
 | 
						|
#include <iostream>
 | 
						|
#include <iomanip>
 | 
						|
#include <string>
 | 
						|
#include <cstring>
 | 
						|
#include <cassert>
 | 
						|
 | 
						|
// ******************************************************
 | 
						|
 | 
						|
namespace openssl {
 | 
						|
#include <openssl/bn.h>
 | 
						|
}
 | 
						|
 | 
						|
namespace arith {
 | 
						|
struct dec_string {
 | 
						|
  std::string str;
 | 
						|
  explicit dec_string(const std::string& s) : str(s) {
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
struct hex_string {
 | 
						|
  std::string str;
 | 
						|
  explicit hex_string(const std::string& s) : str(s) {
 | 
						|
  }
 | 
						|
};
 | 
						|
}  // namespace arith
 | 
						|
 | 
						|
namespace arith {
 | 
						|
 | 
						|
using namespace openssl;
 | 
						|
 | 
						|
inline void bn_assert(int cond);
 | 
						|
BN_CTX* get_ctx();
 | 
						|
 | 
						|
class BignumBitref {
 | 
						|
  BIGNUM* ptr;
 | 
						|
  int n;
 | 
						|
 | 
						|
 public:
 | 
						|
  BignumBitref(BIGNUM& x, int _n) : ptr(&x), n(_n){};
 | 
						|
  operator bool() const {
 | 
						|
    return BN_is_bit_set(ptr, n);
 | 
						|
  }
 | 
						|
  BignumBitref& operator=(bool val);
 | 
						|
};
 | 
						|
 | 
						|
class Bignum {
 | 
						|
  BIGNUM val;
 | 
						|
 | 
						|
 public:
 | 
						|
  class bignum_error {};
 | 
						|
  Bignum() {
 | 
						|
    BN_init(&val);
 | 
						|
  }
 | 
						|
  Bignum(long x) {
 | 
						|
    BN_init(&val);
 | 
						|
    set_long(x);
 | 
						|
  }
 | 
						|
  ~Bignum() {
 | 
						|
    BN_free(&val);
 | 
						|
  }
 | 
						|
  Bignum(const dec_string& ds) {
 | 
						|
    BN_init(&val);
 | 
						|
    set_dec_str(ds.str);
 | 
						|
  }
 | 
						|
  Bignum(const hex_string& hs) {
 | 
						|
    BN_init(&val);
 | 
						|
    set_hex_str(hs.str);
 | 
						|
  }
 | 
						|
  Bignum(const Bignum& x) {
 | 
						|
    BN_init(&val);
 | 
						|
    BN_copy(&val, &x.val);
 | 
						|
  }
 | 
						|
  //Bignum (Bignum&& x) { val = x.val; }
 | 
						|
  void clear() {
 | 
						|
    BN_clear(&val);
 | 
						|
  }  // use this for sensitive data
 | 
						|
  Bignum& operator=(const Bignum& x) {
 | 
						|
    BN_copy(&val, &x.val);
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
  Bignum& operator=(Bignum&& x) {
 | 
						|
    swap(x);
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
  Bignum& operator=(long x) {
 | 
						|
    return set_long(x);
 | 
						|
  }
 | 
						|
  Bignum& operator=(const dec_string& ds) {
 | 
						|
    return set_dec_str(ds.str);
 | 
						|
  }
 | 
						|
  Bignum& operator=(const hex_string& hs) {
 | 
						|
    return set_hex_str(hs.str);
 | 
						|
  }
 | 
						|
  Bignum& swap(Bignum& x) {
 | 
						|
    BN_swap(&val, &x.val);
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
  BIGNUM* bn_ptr() {
 | 
						|
    return &val;
 | 
						|
  }
 | 
						|
  const BIGNUM* bn_ptr() const {
 | 
						|
    return &val;
 | 
						|
  }
 | 
						|
  bool is_zero() const {
 | 
						|
    return BN_is_zero(&val);
 | 
						|
  }
 | 
						|
  int sign() const {
 | 
						|
    return BN_is_zero(&val) ? 0 : (BN_is_negative(&val) ? -1 : 1);
 | 
						|
  }
 | 
						|
  bool odd() const {
 | 
						|
    return BN_is_odd(&val);
 | 
						|
  }
 | 
						|
  int num_bits() const {
 | 
						|
    return BN_num_bits(&val);
 | 
						|
  }
 | 
						|
  int num_bytes() const {
 | 
						|
    return BN_num_bytes(&val);
 | 
						|
  }
 | 
						|
  bool operator[](int n) const {
 | 
						|
    return BN_is_bit_set(&val, n);
 | 
						|
  }
 | 
						|
  BignumBitref operator[](int n) {
 | 
						|
    return BignumBitref(val, n);
 | 
						|
  }
 | 
						|
  void export_msb(unsigned char* buffer, std::size_t size) const;
 | 
						|
  Bignum& import_msb(const unsigned char* buffer, std::size_t size);
 | 
						|
  Bignum& import_msb(const std::string& s) {
 | 
						|
    return import_msb((const unsigned char*)s.c_str(), s.size());
 | 
						|
  }
 | 
						|
  void export_lsb(unsigned char* buffer, std::size_t size) const;
 | 
						|
  Bignum& import_lsb(const unsigned char* buffer, std::size_t size);
 | 
						|
  Bignum& import_lsb(const std::string& s) {
 | 
						|
    return import_lsb((const unsigned char*)s.c_str(), s.size());
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& set_dec_str(std::string s) {
 | 
						|
    BIGNUM* tmp = &val;
 | 
						|
    bn_assert(BN_dec2bn(&tmp, s.c_str()));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& set_hex_str(std::string s) {
 | 
						|
    BIGNUM* tmp = &val;
 | 
						|
    bn_assert(BN_hex2bn(&tmp, s.c_str()));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& set_ulong(unsigned long x) {
 | 
						|
    bn_assert(BN_set_word(&val, x));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& set_long(long x) {
 | 
						|
    set_ulong(std::abs(x));
 | 
						|
    return x < 0 ? negate() : *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& negate() {
 | 
						|
    BN_set_negative(&val, !BN_is_negative(&val));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& operator+=(const Bignum& y) {
 | 
						|
    bn_assert(BN_add(&val, &val, &y.val));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& operator+=(long y) {
 | 
						|
    bn_assert((y >= 0 ? BN_add_word : BN_sub_word)(&val, std::abs(y)));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& operator-=(long y) {
 | 
						|
    bn_assert((y >= 0 ? BN_sub_word : BN_add_word)(&val, std::abs(y)));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& operator*=(const Bignum& y) {
 | 
						|
    bn_assert(BN_mul(&val, &val, &y.val, get_ctx()));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& operator*=(long y) {
 | 
						|
    if (y < 0) {
 | 
						|
      negate();
 | 
						|
    }
 | 
						|
    bn_assert(BN_mul_word(&val, std::abs(y)));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& operator<<=(int r) {
 | 
						|
    bn_assert(BN_lshift(&val, &val, r));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& operator>>=(int r) {
 | 
						|
    bn_assert(BN_rshift(&val, &val, r));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& operator/=(const Bignum& y) {
 | 
						|
    Bignum w;
 | 
						|
    bn_assert(BN_div(&val, &w.val, &val, &y.val, get_ctx()));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& operator/=(long y) {
 | 
						|
    bn_assert(BN_div_word(&val, std::abs(y)) != (BN_ULONG)(-1));
 | 
						|
    return y < 0 ? negate() : *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& operator%=(const Bignum& y) {
 | 
						|
    bn_assert(BN_mod(&val, &val, &y.val, get_ctx()));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& operator%=(long y) {
 | 
						|
    BN_ULONG rem = BN_mod_word(&val, std::abs(y));
 | 
						|
    bn_assert(rem != (BN_ULONG)(-1));
 | 
						|
    return set_long(y < 0 ? -rem : rem);
 | 
						|
  }
 | 
						|
 | 
						|
  unsigned long divmod(unsigned long y) {
 | 
						|
    BN_ULONG rem = BN_div_word(&val, y);
 | 
						|
    bn_assert(rem != (BN_ULONG)(-1));
 | 
						|
    return rem;
 | 
						|
  }
 | 
						|
 | 
						|
  const Bignum divmod(const Bignum& y);
 | 
						|
 | 
						|
  std::string to_str() const;
 | 
						|
  std::string to_hex() const;
 | 
						|
};
 | 
						|
 | 
						|
inline void bn_assert(int cond) {
 | 
						|
  if (!cond) {
 | 
						|
    throw Bignum::bignum_error();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
BN_CTX* get_ctx(void) {
 | 
						|
  static BN_CTX* ctx = BN_CTX_new();
 | 
						|
  return ctx;
 | 
						|
}
 | 
						|
 | 
						|
BignumBitref& BignumBitref::operator=(bool val) {
 | 
						|
  if (val) {
 | 
						|
    BN_set_bit(ptr, n);
 | 
						|
  } else {
 | 
						|
    BN_clear_bit(ptr, n);
 | 
						|
  }
 | 
						|
  return *this;
 | 
						|
}
 | 
						|
 | 
						|
const Bignum operator+(const Bignum& x, const Bignum& y) {
 | 
						|
  Bignum z;
 | 
						|
  bn_assert(BN_add(z.bn_ptr(), x.bn_ptr(), y.bn_ptr()));
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
const Bignum operator+(const Bignum& x, long y) {
 | 
						|
  if (y > 0) {
 | 
						|
    Bignum z(x);
 | 
						|
    bn_assert(BN_add_word(z.bn_ptr(), y));
 | 
						|
    return z;
 | 
						|
  } else if (y < 0) {
 | 
						|
    Bignum z(x);
 | 
						|
    bn_assert(BN_sub_word(z.bn_ptr(), -y));
 | 
						|
    return z;
 | 
						|
  } else {
 | 
						|
    return x;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  const Bignum operator+ (Bignum&& x, long y) {
 | 
						|
    if (y > 0) {
 | 
						|
      bn_assert (BN_add_word (x.bn_ptr(), y));
 | 
						|
    } else if (y < 0) {
 | 
						|
      bn_assert (BN_sub_word (x.bn_ptr(), -y));
 | 
						|
    }
 | 
						|
    return std::move (x);
 | 
						|
  }
 | 
						|
  */
 | 
						|
 | 
						|
const Bignum operator+(long y, const Bignum& x) {
 | 
						|
  return x + y;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  const Bignum operator+ (long y, Bignum&& x) {
 | 
						|
    return x + y;
 | 
						|
  }
 | 
						|
  */
 | 
						|
 | 
						|
const Bignum operator-(const Bignum& x, const Bignum& y) {
 | 
						|
  Bignum z;
 | 
						|
  bn_assert(BN_sub(z.bn_ptr(), x.bn_ptr(), y.bn_ptr()));
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
const Bignum operator-(const Bignum& x, long y) {
 | 
						|
  return x + (-y);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  const Bignum operator- (Bignum&& x, long y) {
 | 
						|
    return x + (-y);
 | 
						|
  }
 | 
						|
  */
 | 
						|
 | 
						|
const Bignum operator*(const Bignum& x, const Bignum& y) {
 | 
						|
  Bignum z;
 | 
						|
  bn_assert(BN_mul(z.bn_ptr(), x.bn_ptr(), y.bn_ptr(), get_ctx()));
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
const Bignum operator*(const Bignum& x, long y) {
 | 
						|
  if (y > 0) {
 | 
						|
    Bignum z(x);
 | 
						|
    bn_assert(BN_mul_word(z.bn_ptr(), y));
 | 
						|
    return z;
 | 
						|
  } else if (y < 0) {
 | 
						|
    Bignum z(x);
 | 
						|
    z.negate();
 | 
						|
    bn_assert(BN_mul_word(z.bn_ptr(), -y));
 | 
						|
    return z;
 | 
						|
  } else {
 | 
						|
    Bignum z(0);
 | 
						|
    return z;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  const Bignum operator* (Bignum&& x, long y) {
 | 
						|
    if (y > 0) {
 | 
						|
      bn_assert (BN_mul_word (x.bn_ptr(), y));
 | 
						|
    } else if (y < 0) {
 | 
						|
      x.negate();
 | 
						|
      bn_assert (BN_mul_word (x.bn_ptr(), -y));
 | 
						|
    } else {
 | 
						|
      x = 0;
 | 
						|
    }
 | 
						|
    return std::move (x);
 | 
						|
  }
 | 
						|
  */
 | 
						|
 | 
						|
const Bignum operator*(long y, const Bignum& x) {
 | 
						|
  return x * y;
 | 
						|
}
 | 
						|
 | 
						|
const Bignum operator/(const Bignum& x, const Bignum& y) {
 | 
						|
  Bignum z, w;
 | 
						|
  bn_assert(BN_div(z.bn_ptr(), w.bn_ptr(), x.bn_ptr(), y.bn_ptr(), get_ctx()));
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
const Bignum Bignum::divmod(const Bignum& y) {
 | 
						|
  Bignum w;
 | 
						|
  bn_assert(BN_div(&val, w.bn_ptr(), &val, y.bn_ptr(), get_ctx()));
 | 
						|
  return w;
 | 
						|
}
 | 
						|
 | 
						|
const Bignum operator%(const Bignum& x, const Bignum& y) {
 | 
						|
  Bignum z;
 | 
						|
  bn_assert(BN_mod(z.bn_ptr(), x.bn_ptr(), y.bn_ptr(), get_ctx()));
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
unsigned long operator%(const Bignum& x, unsigned long y) {
 | 
						|
  BN_ULONG rem = BN_mod_word(x.bn_ptr(), y);
 | 
						|
  bn_assert(rem != (BN_ULONG)(-1));
 | 
						|
  return rem;
 | 
						|
}
 | 
						|
 | 
						|
const Bignum operator<<(const Bignum& x, int r) {
 | 
						|
  Bignum z;
 | 
						|
  bn_assert(BN_lshift(z.bn_ptr(), x.bn_ptr(), r));
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
const Bignum operator>>(const Bignum& x, int r) {
 | 
						|
  Bignum z;
 | 
						|
  bn_assert(BN_rshift(z.bn_ptr(), x.bn_ptr(), r));
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
const Bignum abs(const Bignum& x) {
 | 
						|
  Bignum T(x);
 | 
						|
  if (T.sign() < 0) {
 | 
						|
    T.negate();
 | 
						|
  }
 | 
						|
  return T;
 | 
						|
}
 | 
						|
 | 
						|
const Bignum sqr(const Bignum& x) {
 | 
						|
  Bignum z;
 | 
						|
  bn_assert(BN_sqr(z.bn_ptr(), x.bn_ptr(), get_ctx()));
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
void Bignum::export_msb(unsigned char* buffer, std::size_t size) const {
 | 
						|
  bn_assert(size >= 0 && size <= (1 << 20));
 | 
						|
  bn_assert(sign() >= 0);
 | 
						|
  int n = BN_num_bytes(&val);
 | 
						|
  bn_assert(n >= 0 && (unsigned)n <= size);
 | 
						|
  bn_assert(BN_bn2bin(&val, buffer + size - n) == n);
 | 
						|
  std::memset(buffer, 0, size - n);
 | 
						|
}
 | 
						|
 | 
						|
Bignum& Bignum::import_msb(const unsigned char* buffer, std::size_t size) {
 | 
						|
  bn_assert(size >= 0 && size <= (1 << 20));
 | 
						|
  std::size_t i = 0;
 | 
						|
  while (i < size && !buffer[i]) {
 | 
						|
    i++;
 | 
						|
  }
 | 
						|
  bn_assert(BN_bin2bn(buffer + i, size - i, &val) == &val);
 | 
						|
  return *this;
 | 
						|
}
 | 
						|
 | 
						|
void Bignum::export_lsb(unsigned char* buffer, std::size_t size) const {
 | 
						|
  bn_assert(size >= 0 && size <= (1 << 20));
 | 
						|
  bn_assert(sign() >= 0);
 | 
						|
  std::size_t n = BN_num_bytes(&val);
 | 
						|
  bn_assert(n >= 0 && (unsigned)n <= size);
 | 
						|
  bn_assert(BN_bn2bin(&val, buffer) == (int)n);
 | 
						|
  std::memset(buffer + n, 0, size - n);
 | 
						|
  for (std::size_t i = 0; 2 * i + 1 < n; i++) {
 | 
						|
    std::swap(buffer[i], buffer[n - 1 - i]);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
Bignum& Bignum::import_lsb(const unsigned char* buffer, std::size_t size) {
 | 
						|
  bn_assert(size >= 0 && size <= (1 << 20));
 | 
						|
  while (size > 0 && !buffer[size - 1]) {
 | 
						|
    size--;
 | 
						|
  }
 | 
						|
  if (!size) {
 | 
						|
    bn_assert(BN_zero(&val));
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
  unsigned char tmp[size], *ptr = tmp + size;
 | 
						|
  for (std::size_t i = 0; i < size; i++) {
 | 
						|
    *--ptr = buffer[i];
 | 
						|
  }
 | 
						|
  bn_assert(BN_bin2bn(tmp, size, &val) == &val);
 | 
						|
  return *this;
 | 
						|
}
 | 
						|
 | 
						|
int cmp(const Bignum& x, const Bignum& y) {
 | 
						|
  return BN_cmp(x.bn_ptr(), y.bn_ptr());
 | 
						|
}
 | 
						|
 | 
						|
bool operator==(const Bignum& x, const Bignum& y) {
 | 
						|
  return cmp(x, y) == 0;
 | 
						|
}
 | 
						|
 | 
						|
bool operator!=(const Bignum& x, const Bignum& y) {
 | 
						|
  return cmp(x, y) != 0;
 | 
						|
}
 | 
						|
 | 
						|
bool operator<(const Bignum& x, const Bignum& y) {
 | 
						|
  return cmp(x, y) < 0;
 | 
						|
}
 | 
						|
 | 
						|
bool operator<=(const Bignum& x, const Bignum& y) {
 | 
						|
  return cmp(x, y) <= 0;
 | 
						|
}
 | 
						|
 | 
						|
bool operator>(const Bignum& x, const Bignum& y) {
 | 
						|
  return cmp(x, y) > 0;
 | 
						|
}
 | 
						|
 | 
						|
bool operator>=(const Bignum& x, const Bignum& y) {
 | 
						|
  return cmp(x, y) >= 0;
 | 
						|
}
 | 
						|
 | 
						|
bool operator==(const Bignum& x, long y) {
 | 
						|
  if (y >= 0) {
 | 
						|
    return BN_is_word(x.bn_ptr(), y);
 | 
						|
  } else {
 | 
						|
    return x == Bignum(y);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool operator!=(const Bignum& x, long y) {
 | 
						|
  if (y >= 0) {
 | 
						|
    return !BN_is_word(x.bn_ptr(), y);
 | 
						|
  } else {
 | 
						|
    return x != Bignum(y);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
std::string Bignum::to_str() const {
 | 
						|
  char* ptr = BN_bn2dec(&val);
 | 
						|
  std::string z(ptr);
 | 
						|
  OPENSSL_free(ptr);
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
std::string Bignum::to_hex() const {
 | 
						|
  char* ptr = BN_bn2hex(&val);
 | 
						|
  std::string z(ptr);
 | 
						|
  OPENSSL_free(ptr);
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
std::ostream& operator<<(std::ostream& os, const Bignum& x) {
 | 
						|
  return os << x.to_str();
 | 
						|
}
 | 
						|
 | 
						|
std::istream& operator>>(std::istream& is, Bignum& x) {
 | 
						|
  std::string word;
 | 
						|
  is >> word;
 | 
						|
  x = dec_string(word);
 | 
						|
  return is;
 | 
						|
}
 | 
						|
 | 
						|
bool is_prime(const Bignum& p, int nchecks = 64, bool trial_div = true) {
 | 
						|
  return BN_is_prime_fasttest_ex(p.bn_ptr(), BN_prime_checks, get_ctx(), trial_div, 0);
 | 
						|
}
 | 
						|
}  // namespace arith
 | 
						|
 | 
						|
namespace arith {
 | 
						|
using namespace openssl;
 | 
						|
 | 
						|
class Residue;
 | 
						|
class ResidueRing;
 | 
						|
 | 
						|
class ResidueRing {
 | 
						|
 public:
 | 
						|
  struct bad_modulus {};
 | 
						|
  struct elem_cnt_mismatch {
 | 
						|
    int cnt;
 | 
						|
    elem_cnt_mismatch(int x) : cnt(x) {
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
 private:
 | 
						|
  const Bignum modulus;
 | 
						|
  mutable int cnt;
 | 
						|
  bool prime;
 | 
						|
  void cnt_assert(bool b) {
 | 
						|
    if (!b) {
 | 
						|
      throw elem_cnt_mismatch(cnt);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  Residue* Zero;
 | 
						|
  Residue* One;
 | 
						|
  Residue* Img_i;
 | 
						|
  void init();
 | 
						|
 | 
						|
 public:
 | 
						|
  typedef Residue element;
 | 
						|
  explicit ResidueRing(Bignum mod) : modulus(mod), cnt(0), prime(arith::is_prime(mod)), Zero(0), One(0) {
 | 
						|
    init();
 | 
						|
  }
 | 
						|
  ~ResidueRing();
 | 
						|
  int incr_count() {
 | 
						|
    return ++cnt;
 | 
						|
  }
 | 
						|
  int decr_count() {
 | 
						|
    --cnt;
 | 
						|
    cnt_assert(cnt >= 0);
 | 
						|
    return cnt;
 | 
						|
  }
 | 
						|
  const Bignum& get_modulus() const {
 | 
						|
    return modulus;
 | 
						|
  }
 | 
						|
  bool is_prime() const {
 | 
						|
    return prime;
 | 
						|
  }
 | 
						|
  const Residue& zero() const {
 | 
						|
    return *Zero;
 | 
						|
  }
 | 
						|
  const Residue& one() const {
 | 
						|
    return *One;
 | 
						|
  }
 | 
						|
  const Residue& img_i();
 | 
						|
  Residue frac(long num, long denom = 1);
 | 
						|
  Residue convert(long num);
 | 
						|
  Residue convert(const Bignum& x);
 | 
						|
 | 
						|
  Bignum reduce(const Bignum& x) {
 | 
						|
    Bignum r = x % modulus;
 | 
						|
    if (r.sign() < 0) {
 | 
						|
      r += modulus;
 | 
						|
    }
 | 
						|
    return r;
 | 
						|
  }
 | 
						|
 | 
						|
  Bignum& do_reduce(Bignum& x) {
 | 
						|
    x %= modulus;
 | 
						|
    if (x.sign() < 0) {
 | 
						|
      x += modulus;
 | 
						|
    }
 | 
						|
    return x;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
class Residue {
 | 
						|
 public:
 | 
						|
  struct not_same_ring {};
 | 
						|
 | 
						|
 private:
 | 
						|
  ResidueRing* ring;
 | 
						|
  mutable Bignum val;
 | 
						|
  Residue& reduce() {
 | 
						|
    ring->do_reduce(val);
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
 public:
 | 
						|
  explicit Residue(ResidueRing& R) : ring(&R) {
 | 
						|
    R.incr_count();
 | 
						|
  }
 | 
						|
  Residue(const Bignum& x, ResidueRing& R) : ring(&R), val(R.reduce(x)) {
 | 
						|
    R.incr_count();
 | 
						|
  }
 | 
						|
  ~Residue() {
 | 
						|
    ring->decr_count();
 | 
						|
    ring = 0;
 | 
						|
  }
 | 
						|
  Residue(const Residue& x) : ring(x.ring), val(x.val) {
 | 
						|
    ring->incr_count();
 | 
						|
  }
 | 
						|
  Bignum extract() const {
 | 
						|
    return val;
 | 
						|
  }
 | 
						|
  const Bignum& extract_raw() const {
 | 
						|
    return val;
 | 
						|
  }
 | 
						|
  const Bignum& modulus() const {
 | 
						|
    return ring->get_modulus();
 | 
						|
  }
 | 
						|
  void same_ring(const Residue& y) const {
 | 
						|
    if (ring != y.ring) {
 | 
						|
      throw not_same_ring();
 | 
						|
    }
 | 
						|
  }
 | 
						|
  ResidueRing& ring_of() const {
 | 
						|
    return *ring;
 | 
						|
  }
 | 
						|
  bool is_zero() const {
 | 
						|
    return (val == 0);
 | 
						|
  }
 | 
						|
  Residue& operator=(const Residue& x) {
 | 
						|
    same_ring(x);
 | 
						|
    val = x.val;
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
  Residue& operator=(const Bignum& x) {
 | 
						|
    val = ring->reduce(x);
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
  Residue& operator+=(const Residue& y);
 | 
						|
  Residue& operator-=(const Residue& y);
 | 
						|
  Residue& operator*=(const Residue& y);
 | 
						|
  Residue& operator+=(long y) {
 | 
						|
    val += y;
 | 
						|
    return reduce();
 | 
						|
  }
 | 
						|
  Residue& operator-=(long y) {
 | 
						|
    val -= y;
 | 
						|
    return reduce();
 | 
						|
  }
 | 
						|
  Residue& operator*=(long y) {
 | 
						|
    val *= y;
 | 
						|
    return reduce();
 | 
						|
  }
 | 
						|
  Residue& negate() {
 | 
						|
    val.negate();
 | 
						|
    return reduce();
 | 
						|
  }
 | 
						|
  friend const Residue operator+(const Residue& x, const Residue& y);
 | 
						|
  friend const Residue operator-(const Residue& x, const Residue& y);
 | 
						|
  friend const Residue operator*(const Residue& x, const Residue& y);
 | 
						|
  friend const Residue operator-(const Residue& x);
 | 
						|
  friend Residue sqr(const Residue& x);
 | 
						|
  friend Residue power(const Residue& x, const Bignum& y);
 | 
						|
  friend Residue inverse(const Residue& x);
 | 
						|
  std::string to_str() const;
 | 
						|
};
 | 
						|
 | 
						|
void ResidueRing::init() {
 | 
						|
  Zero = new Residue(0, *this);
 | 
						|
  One = new Residue(1, *this);
 | 
						|
}
 | 
						|
 | 
						|
ResidueRing::~ResidueRing() {
 | 
						|
  delete Zero;
 | 
						|
  delete One;
 | 
						|
  Zero = One = 0;
 | 
						|
  cnt_assert(!cnt);
 | 
						|
}
 | 
						|
 | 
						|
const Residue operator+(const Residue& x, const Residue& y) {
 | 
						|
  x.same_ring(y);
 | 
						|
  Residue z(x.ring_of());
 | 
						|
  bn_assert(BN_mod_add(z.val.bn_ptr(), x.val.bn_ptr(), y.val.bn_ptr(), x.modulus().bn_ptr(), get_ctx()));
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
const Residue operator-(const Residue& x, const Residue& y) {
 | 
						|
  x.same_ring(y);
 | 
						|
  Residue z(x.ring_of());
 | 
						|
  bn_assert(BN_mod_sub(z.val.bn_ptr(), x.val.bn_ptr(), y.val.bn_ptr(), x.modulus().bn_ptr(), get_ctx()));
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
const Residue operator*(const Residue& x, const Residue& y) {
 | 
						|
  x.same_ring(y);
 | 
						|
  Residue z(x.ring_of());
 | 
						|
  bn_assert(BN_mod_mul(z.val.bn_ptr(), x.val.bn_ptr(), y.val.bn_ptr(), x.modulus().bn_ptr(), get_ctx()));
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
const Residue operator-(const Residue& x) {
 | 
						|
  Residue z(x);
 | 
						|
  z.val.negate();
 | 
						|
  return z.reduce();
 | 
						|
}
 | 
						|
 | 
						|
Residue& Residue::operator+=(const Residue& y) {
 | 
						|
  same_ring(y);
 | 
						|
  bn_assert(BN_mod_add(val.bn_ptr(), val.bn_ptr(), y.val.bn_ptr(), modulus().bn_ptr(), get_ctx()));
 | 
						|
  return *this;
 | 
						|
}
 | 
						|
 | 
						|
Residue& Residue::operator-=(const Residue& y) {
 | 
						|
  same_ring(y);
 | 
						|
  bn_assert(BN_mod_sub(val.bn_ptr(), val.bn_ptr(), y.val.bn_ptr(), modulus().bn_ptr(), get_ctx()));
 | 
						|
  return *this;
 | 
						|
}
 | 
						|
 | 
						|
Residue& Residue::operator*=(const Residue& y) {
 | 
						|
  same_ring(y);
 | 
						|
  bn_assert(BN_mod_mul(val.bn_ptr(), val.bn_ptr(), y.val.bn_ptr(), modulus().bn_ptr(), get_ctx()));
 | 
						|
  return *this;
 | 
						|
}
 | 
						|
 | 
						|
bool operator==(const Residue& x, const Residue& y) {
 | 
						|
  x.same_ring(y);
 | 
						|
  return x.extract() == y.extract();
 | 
						|
}
 | 
						|
 | 
						|
bool operator!=(const Residue& x, const Residue& y) {
 | 
						|
  x.same_ring(y);
 | 
						|
  return x.extract() != y.extract();
 | 
						|
}
 | 
						|
 | 
						|
Residue sqr(const Residue& x) {
 | 
						|
  Residue z(x.ring_of());
 | 
						|
  bn_assert(BN_mod_sqr(z.val.bn_ptr(), x.val.bn_ptr(), x.modulus().bn_ptr(), get_ctx()));
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
Residue power(const Residue& x, const Bignum& y) {
 | 
						|
  Residue z(x.ring_of());
 | 
						|
  bn_assert(BN_mod_exp(z.val.bn_ptr(), x.val.bn_ptr(), y.bn_ptr(), x.modulus().bn_ptr(), get_ctx()));
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
Residue inverse(const Residue& x) {
 | 
						|
  assert(x.ring_of().is_prime());
 | 
						|
  return power(x, x.ring_of().get_modulus() - 2);
 | 
						|
}
 | 
						|
 | 
						|
const Residue& ResidueRing::img_i() {
 | 
						|
  if (!Img_i) {
 | 
						|
    assert(is_prime());
 | 
						|
    assert(modulus % 4 == 1);
 | 
						|
    int g = 2;
 | 
						|
    Bignum n = (modulus - 1) / 4;
 | 
						|
    while (true) {
 | 
						|
      Residue t = power(frac(g), n);
 | 
						|
      if (t != one() && t != frac(-1)) {
 | 
						|
        Img_i = new Residue(t);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return *Img_i;
 | 
						|
}
 | 
						|
 | 
						|
Residue sqrt(const Residue& x) {
 | 
						|
  assert(x.ring_of().is_prime());
 | 
						|
  ResidueRing& R = x.ring_of();
 | 
						|
  const Bignum& p = R.get_modulus();
 | 
						|
  if (x.is_zero() || !p.odd()) {
 | 
						|
    return x;
 | 
						|
  }
 | 
						|
  if (p[1]) {  // p=3 (mod 4)
 | 
						|
    return power(x, (p + 1) >> 2);
 | 
						|
  } else if (p[2]) {
 | 
						|
    // p=5 (mod 8)
 | 
						|
    Residue t = power(x, (p + 3) >> 3);
 | 
						|
    return (sqr(t) == x) ? t : R.img_i() * t;
 | 
						|
  } else {
 | 
						|
    assert(p[2]);
 | 
						|
    return R.zero();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
Residue ResidueRing::frac(long num, long denom) {
 | 
						|
  assert(denom);
 | 
						|
  if (denom < 0) {
 | 
						|
    num = -num;
 | 
						|
    denom = -denom;
 | 
						|
  }
 | 
						|
  if (!(num % denom)) {
 | 
						|
    return Residue(num / denom, *this);
 | 
						|
  } else {
 | 
						|
    return Residue(num, *this) * inverse(Residue(denom, *this));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
inline Residue ResidueRing::convert(long x) {
 | 
						|
  return Residue(x, *this);
 | 
						|
}
 | 
						|
 | 
						|
inline Residue ResidueRing::convert(const Bignum& x) {
 | 
						|
  return Residue(x, *this);
 | 
						|
}
 | 
						|
 | 
						|
std::string Residue::to_str() const {
 | 
						|
  return "Mod(" + val.to_str() + "," + modulus().to_str() + ")";
 | 
						|
}
 | 
						|
 | 
						|
std::ostream& operator<<(std::ostream& os, const Residue& x) {
 | 
						|
  return os << x.to_str();
 | 
						|
}
 | 
						|
 | 
						|
std::istream& operator>>(std::istream& is, Residue& x) {
 | 
						|
  std::string word;
 | 
						|
  is >> word;
 | 
						|
  x = dec_string(word);
 | 
						|
  return is;
 | 
						|
}
 | 
						|
}  // namespace arith
 | 
						|
 | 
						|
// ******************************************************
 | 
						|
 | 
						|
namespace ellcurve {
 | 
						|
using namespace arith;
 | 
						|
 | 
						|
const Bignum& P25519() {
 | 
						|
  static Bignum P25519 = (Bignum(1) << 255) - 19;
 | 
						|
  return P25519;
 | 
						|
}
 | 
						|
 | 
						|
ResidueRing& Fp25519() {
 | 
						|
  static ResidueRing Fp25519(P25519());
 | 
						|
  return Fp25519;
 | 
						|
}
 | 
						|
}  // namespace ellcurve
 | 
						|
 | 
						|
// ******************************************************
 | 
						|
 | 
						|
namespace ellcurve {
 | 
						|
using namespace arith;
 | 
						|
 | 
						|
class MontgomeryCurve {
 | 
						|
  ResidueRing& ring;
 | 
						|
  int A_short;   // v^2 = u^2 + Au + 1
 | 
						|
  int Gu_short;  // u(G)
 | 
						|
  int a_short;   // (A+2)/4
 | 
						|
  Residue A;
 | 
						|
  Residue Gu;
 | 
						|
  Bignum P;
 | 
						|
  Bignum L;
 | 
						|
  Bignum Order;
 | 
						|
  Bignum cofactor;
 | 
						|
  int cofactor_short;
 | 
						|
 | 
						|
  void init();
 | 
						|
 | 
						|
 public:
 | 
						|
  MontgomeryCurve(int _A, int _Gu, ResidueRing& _R)
 | 
						|
      : ring(_R)
 | 
						|
      , A_short(_A)
 | 
						|
      , Gu_short(_Gu)
 | 
						|
      , a_short((_A + 2) / 4)
 | 
						|
      , A(_A, _R)
 | 
						|
      , Gu(_Gu, _R)
 | 
						|
      , P(_R.get_modulus())
 | 
						|
      , cofactor_short(0) {
 | 
						|
    init();
 | 
						|
  }
 | 
						|
 | 
						|
  const Residue& get_gen_u() const {
 | 
						|
    return Gu;
 | 
						|
  }
 | 
						|
  const Bignum& get_ell() const {
 | 
						|
    return L;
 | 
						|
  }
 | 
						|
  const Bignum& get_order() const {
 | 
						|
    return Order;
 | 
						|
  }
 | 
						|
  ResidueRing& get_base_ring() const {
 | 
						|
    return ring;
 | 
						|
  }
 | 
						|
  const Bignum& get_p() const {
 | 
						|
    return P;
 | 
						|
  }
 | 
						|
 | 
						|
  void set_order_cofactor(const Bignum& order, int cof);
 | 
						|
 | 
						|
  struct PointXZ {
 | 
						|
    Residue X, Z;
 | 
						|
    PointXZ(Residue x, Residue z) : X(x), Z(z) {
 | 
						|
      x.same_ring(z);
 | 
						|
    }
 | 
						|
    PointXZ(ResidueRing& r) : X(r.one()), Z(r.zero()) {
 | 
						|
    }
 | 
						|
    explicit PointXZ(Residue u) : X(u), Z(u.ring_of().one()) {
 | 
						|
    }
 | 
						|
    explicit PointXZ(Residue y, bool) : X(y.ring_of().one() - y), Z(y + y.ring_of().one()) {
 | 
						|
    }
 | 
						|
    PointXZ(const PointXZ& P) : X(P.X), Z(P.Z) {
 | 
						|
    }
 | 
						|
    PointXZ& operator=(const PointXZ& P) {
 | 
						|
      X = P.X;
 | 
						|
      Z = P.Z;
 | 
						|
      return *this;
 | 
						|
    }
 | 
						|
    Residue get_u() const {
 | 
						|
      return X * inverse(Z);
 | 
						|
    }
 | 
						|
    Residue get_v(bool sign_v = false) const;
 | 
						|
    bool is_infty() const {
 | 
						|
      return Z.is_zero();
 | 
						|
    }
 | 
						|
    Residue get_y() const {
 | 
						|
      return (X - Z) * inverse(X + Z);
 | 
						|
    }
 | 
						|
    bool export_point_y(unsigned char buffer[32]) const;
 | 
						|
    bool export_point_u(unsigned char buffer[32]) const;
 | 
						|
    void zeroize() {
 | 
						|
      X = Z = Z.ring_of().zero();
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  PointXZ power_gen_xz(const Bignum& n) const;
 | 
						|
  PointXZ power_xz(const Residue& u, const Bignum& n) const;
 | 
						|
  PointXZ power_xz(const PointXZ& P, const Bignum& n) const;
 | 
						|
  PointXZ add_xz(const PointXZ& P, const PointXZ& Q) const;
 | 
						|
  PointXZ double_xz(const PointXZ& P) const;
 | 
						|
 | 
						|
  PointXZ import_point_u(const unsigned char point[32]) const;
 | 
						|
  PointXZ import_point_y(const unsigned char point[32]) const;
 | 
						|
};
 | 
						|
 | 
						|
void MontgomeryCurve::init() {
 | 
						|
  assert(!((a_short + 2) & 3) && a_short >= 0);
 | 
						|
}
 | 
						|
 | 
						|
void MontgomeryCurve::set_order_cofactor(const Bignum& order, int cof) {
 | 
						|
  assert(order > 0);
 | 
						|
  assert(cof >= 0);
 | 
						|
  assert(cof == 0 || (order % cof) == 0);
 | 
						|
  Order = order;
 | 
						|
  cofactor = cofactor_short = cof;
 | 
						|
  if (cof > 0) {
 | 
						|
    L = order / cof;
 | 
						|
    assert(is_prime(L));
 | 
						|
  }
 | 
						|
  assert(!power_gen_xz(1).is_infty());
 | 
						|
  assert(power_gen_xz(Order).is_infty());
 | 
						|
}
 | 
						|
 | 
						|
// computes u(P+Q)*u(P-Q) as X/Z
 | 
						|
MontgomeryCurve::PointXZ MontgomeryCurve::add_xz(const MontgomeryCurve::PointXZ& P,
 | 
						|
                                                 const MontgomeryCurve::PointXZ& Q) const {
 | 
						|
  Residue u = (P.X + P.Z) * (Q.X - Q.Z);
 | 
						|
  Residue v = (P.X - P.Z) * (Q.X + Q.Z);
 | 
						|
  return MontgomeryCurve::PointXZ(sqr(u + v), sqr(u - v));
 | 
						|
}
 | 
						|
 | 
						|
// computes u(2P) as X/Z
 | 
						|
MontgomeryCurve::PointXZ MontgomeryCurve::double_xz(const MontgomeryCurve::PointXZ& P) const {
 | 
						|
  Residue u = sqr(P.X + P.Z);
 | 
						|
  Residue v = sqr(P.X - P.Z);
 | 
						|
  Residue w = u - v;
 | 
						|
  return PointXZ(u * v, w * (v + Residue(a_short, ring) * w));
 | 
						|
}
 | 
						|
 | 
						|
MontgomeryCurve::PointXZ MontgomeryCurve::power_gen_xz(const Bignum& n) const {
 | 
						|
  return power_xz(Gu, n);
 | 
						|
}
 | 
						|
 | 
						|
MontgomeryCurve::PointXZ MontgomeryCurve::power_xz(const Residue& u, const Bignum& n) const {
 | 
						|
  return power_xz(PointXZ(u), n);
 | 
						|
}
 | 
						|
 | 
						|
// computes u([n]P) in form X/Z
 | 
						|
MontgomeryCurve::PointXZ MontgomeryCurve::power_xz(const PointXZ& A, const Bignum& n) const {
 | 
						|
  assert(n >= 0);
 | 
						|
  if (n == 0) {
 | 
						|
    return PointXZ(ring);
 | 
						|
  }
 | 
						|
 | 
						|
  int k = n.num_bits();
 | 
						|
  PointXZ P(A);
 | 
						|
  PointXZ Q(double_xz(P));
 | 
						|
  for (int i = k - 2; i >= 0; --i) {
 | 
						|
    PointXZ PQ(add_xz(P, Q));
 | 
						|
    PQ.X *= A.Z;
 | 
						|
    PQ.Z *= A.X;
 | 
						|
    if (n[i]) {
 | 
						|
      P = PQ;
 | 
						|
      Q = double_xz(Q);
 | 
						|
    } else {
 | 
						|
      Q = PQ;
 | 
						|
      P = double_xz(P);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return P;
 | 
						|
}
 | 
						|
 | 
						|
bool MontgomeryCurve::PointXZ::export_point_y(unsigned char buffer[32]) const {
 | 
						|
  if ((X + Z).is_zero()) {
 | 
						|
    std::memset(buffer, 0xff, 32);
 | 
						|
    return false;
 | 
						|
  } else {
 | 
						|
    get_y().extract().export_lsb(buffer, 32);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool MontgomeryCurve::PointXZ::export_point_u(unsigned char buffer[32]) const {
 | 
						|
  if (Z.is_zero()) {
 | 
						|
    std::memset(buffer, 0xff, 32);
 | 
						|
    return false;
 | 
						|
  } else {
 | 
						|
    get_u().extract().export_lsb(buffer, 32);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
MontgomeryCurve::PointXZ MontgomeryCurve::import_point_u(const unsigned char point[32]) const {
 | 
						|
  Bignum u;
 | 
						|
  u.import_lsb(point, 32);
 | 
						|
  u[255] = 0;
 | 
						|
  return PointXZ(Residue(u, ring));
 | 
						|
}
 | 
						|
 | 
						|
MontgomeryCurve::PointXZ MontgomeryCurve::import_point_y(const unsigned char point[32]) const {
 | 
						|
  Bignum y;
 | 
						|
  y.import_lsb(point, 32);
 | 
						|
  y[255] = 0;
 | 
						|
  return PointXZ(Residue(y, ring), true);
 | 
						|
}
 | 
						|
 | 
						|
MontgomeryCurve& Curve25519() {
 | 
						|
  static MontgomeryCurve Curve25519(486662, 9, Fp25519());
 | 
						|
  static bool init = false;
 | 
						|
  if (!init) {
 | 
						|
    Curve25519.set_order_cofactor(hex_string{"80000000000000000000000000000000a6f7cef517bce6b2c09318d2e7ae9f68"}, 8);
 | 
						|
    init = true;
 | 
						|
  }
 | 
						|
  return Curve25519;
 | 
						|
}
 | 
						|
}  // namespace ellcurve
 | 
						|
 | 
						|
// ******************************************************
 | 
						|
 | 
						|
namespace ellcurve {
 | 
						|
using namespace arith;
 | 
						|
 | 
						|
class TwEdwardsCurve;
 | 
						|
 | 
						|
class TwEdwardsCurve {
 | 
						|
 public:
 | 
						|
  struct SegrePoint {
 | 
						|
    Residue XY, X, Y, Z;  // if x=X/Z and y=Y/T, stores (xy,x,y,1)*Z*T
 | 
						|
    SegrePoint(ResidueRing& R) : XY(R), X(R), Y(R), Z(R) {
 | 
						|
    }
 | 
						|
    SegrePoint(const Residue& x, const Residue& y) : XY(x * y), X(x), Y(y), Z(y.ring_of().one()) {
 | 
						|
    }
 | 
						|
    SegrePoint(const TwEdwardsCurve& E, const Residue& y, bool x_sign);
 | 
						|
    SegrePoint(const SegrePoint& P) : XY(P.XY), X(P.X), Y(P.Y), Z(P.Z) {
 | 
						|
    }
 | 
						|
    SegrePoint& operator=(const SegrePoint& P) {
 | 
						|
      XY = P.XY;
 | 
						|
      X = P.X;
 | 
						|
      Y = P.Y;
 | 
						|
      Z = P.Z;
 | 
						|
      return *this;
 | 
						|
    }
 | 
						|
    bool is_zero() const {
 | 
						|
      return X.is_zero() && (Y == Z);
 | 
						|
    }
 | 
						|
    bool is_valid() const {
 | 
						|
      return (XY * Z == X * Y) && !(XY.is_zero() && X.is_zero() && Y.is_zero() && Z.is_zero());
 | 
						|
    }
 | 
						|
    bool is_finite() const {
 | 
						|
      return !Z.is_zero();
 | 
						|
    }
 | 
						|
    bool is_normalized() const {
 | 
						|
      return Z == Z.ring_of().one();
 | 
						|
    }
 | 
						|
    SegrePoint& normalize() {
 | 
						|
      auto f = inverse(Z);
 | 
						|
      XY *= f;
 | 
						|
      X *= f;
 | 
						|
      Y *= f;
 | 
						|
      Z = Z.ring_of().one();
 | 
						|
      return *this;
 | 
						|
    }
 | 
						|
    SegrePoint& zeroize() {
 | 
						|
      XY = X = Y = Z = Z.ring_of().zero();
 | 
						|
      return *this;
 | 
						|
    }
 | 
						|
    bool export_point(unsigned char buffer[32], bool need_x = true) const;
 | 
						|
    bool export_point_y(unsigned char buffer[32]) const {
 | 
						|
      return export_point(buffer, false);
 | 
						|
    }
 | 
						|
    bool export_point_u(unsigned char buffer[32]) const;
 | 
						|
    Residue get_y() const {
 | 
						|
      return Y * inverse(Z);
 | 
						|
    }
 | 
						|
    Residue get_x() const {
 | 
						|
      return X * inverse(Z);
 | 
						|
    }
 | 
						|
    Residue get_u() const {
 | 
						|
      return (Z + Y) * inverse(Z - Y);
 | 
						|
    }
 | 
						|
    void negate() {
 | 
						|
      XY.negate();
 | 
						|
      X.negate();
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
 private:
 | 
						|
  ResidueRing& ring;
 | 
						|
  Residue D;
 | 
						|
  Residue D2;
 | 
						|
  Residue Gy;
 | 
						|
  Bignum P;
 | 
						|
  Bignum L;
 | 
						|
  Bignum Order;
 | 
						|
  Bignum cofactor;
 | 
						|
  int cofactor_short;
 | 
						|
  SegrePoint G;
 | 
						|
  SegrePoint O;
 | 
						|
  void init();
 | 
						|
 | 
						|
 public:
 | 
						|
  TwEdwardsCurve(const Residue& _D, const Residue& _Gy, ResidueRing& _R)
 | 
						|
      : ring(_R), D(_D), D2(_D + _D), Gy(_Gy), P(_R.get_modulus()), cofactor_short(0), G(_R), O(_R) {
 | 
						|
    init();
 | 
						|
  }
 | 
						|
 | 
						|
  const Residue& get_gen_y() const {
 | 
						|
    return Gy;
 | 
						|
  }
 | 
						|
  const Bignum& get_ell() const {
 | 
						|
    return L;
 | 
						|
  }
 | 
						|
  const Bignum& get_order() const {
 | 
						|
    return Order;
 | 
						|
  }
 | 
						|
  ResidueRing& get_base_ring() const {
 | 
						|
    return ring;
 | 
						|
  }
 | 
						|
  const Bignum& get_p() const {
 | 
						|
    return P;
 | 
						|
  }
 | 
						|
  const SegrePoint& get_base_point() const {
 | 
						|
    return G;
 | 
						|
  }
 | 
						|
 | 
						|
  void set_order_cofactor(const Bignum& order, int cof);
 | 
						|
  bool recover_x(Residue& x, const Residue& y, bool x_sign) const;
 | 
						|
 | 
						|
  void add_points(SegrePoint& R, const SegrePoint& P, const SegrePoint& Q) const;
 | 
						|
  SegrePoint add_points(const SegrePoint& P, const SegrePoint& Q) const;
 | 
						|
  void double_point(SegrePoint& R, const SegrePoint& P) const;
 | 
						|
  SegrePoint double_point(const SegrePoint& P) const;
 | 
						|
  SegrePoint power_point(const SegrePoint& A, const Bignum& n) const;
 | 
						|
  SegrePoint power_gen(const Bignum& n) const;
 | 
						|
 | 
						|
  SegrePoint import_point(const unsigned char point[32], bool& ok) const;
 | 
						|
};
 | 
						|
 | 
						|
std::ostream& operator<<(std::ostream& os, const TwEdwardsCurve::SegrePoint& P) {
 | 
						|
  return os << "[" << P.XY << ":" << P.X << ":" << P.Y << ":" << P.Z << "]";
 | 
						|
}
 | 
						|
 | 
						|
void TwEdwardsCurve::init() {
 | 
						|
  assert(D != ring.zero() && D != ring.convert(-1));
 | 
						|
  O.X = O.Z = ring.one();
 | 
						|
  G = SegrePoint(*this, Gy, 0);
 | 
						|
  assert(!G.XY.is_zero());
 | 
						|
}
 | 
						|
 | 
						|
void TwEdwardsCurve::set_order_cofactor(const Bignum& order, int cof) {
 | 
						|
  assert(order > 0);
 | 
						|
  assert(cof >= 0);
 | 
						|
  assert(cof == 0 || (order % cof) == 0);
 | 
						|
  Order = order;
 | 
						|
  cofactor = cofactor_short = cof;
 | 
						|
  if (cof > 0) {
 | 
						|
    L = order / cof;
 | 
						|
    assert(is_prime(L));
 | 
						|
    assert(!power_gen(1).is_zero());
 | 
						|
    assert(power_gen(L).is_zero());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
TwEdwardsCurve::SegrePoint::SegrePoint(const TwEdwardsCurve& E, const Residue& y, bool x_sign)
 | 
						|
    : XY(y), X(E.get_base_ring()), Y(y), Z(E.get_base_ring().one()) {
 | 
						|
  Residue x(y.ring_of());
 | 
						|
  if (E.recover_x(x, y, x_sign)) {
 | 
						|
    XY *= x;
 | 
						|
    X = x;
 | 
						|
  } else {
 | 
						|
    XY = Y = Z = E.get_base_ring().zero();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool TwEdwardsCurve::recover_x(Residue& x, const Residue& y, bool x_sign) const {
 | 
						|
  // recovers x from equation -x^2+y^2 = 1+d*x^2*y^2
 | 
						|
  Residue z = inverse(ring.one() + D * sqr(y));
 | 
						|
  if (z.is_zero()) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  z *= sqr(y) - ring.one();
 | 
						|
  Residue t = sqrt(z);
 | 
						|
  if (sqr(t) == z) {
 | 
						|
    x = (t.extract().odd() == x_sign) ? t : -t;
 | 
						|
    //std::cout << "x=" << x << ", y=" << y << std::endl;
 | 
						|
    return true;
 | 
						|
  } else {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void TwEdwardsCurve::add_points(SegrePoint& Res, const SegrePoint& P, const SegrePoint& Q) const {
 | 
						|
  Residue a((P.X + P.Y) * (Q.X + Q.Y));
 | 
						|
  Residue b((P.X - P.Y) * (Q.X - Q.Y));
 | 
						|
  Residue c(P.Z * Q.Z * ring.convert(2));
 | 
						|
  Residue d(P.XY * Q.XY * D2);
 | 
						|
  Residue x_num(a - b);   // 2(x1y2+x2y1)
 | 
						|
  Residue y_num(a + b);   // 2(x1x2+y1y2)
 | 
						|
  Residue x_den(c + d);   // 2(1+dx1x2y1y2)
 | 
						|
  Residue y_den(c - d);   // 2(1-dx1x2y1y2)
 | 
						|
  Res.X = x_num * y_den;  // x = x_num/x_den, y = y_num/y_den
 | 
						|
  Res.Y = y_num * x_den;
 | 
						|
  Res.XY = x_num * y_num;
 | 
						|
  Res.Z = x_den * y_den;
 | 
						|
}
 | 
						|
 | 
						|
TwEdwardsCurve::SegrePoint TwEdwardsCurve::add_points(const SegrePoint& P, const SegrePoint& Q) const {
 | 
						|
  SegrePoint Res(ring);
 | 
						|
  add_points(Res, P, Q);
 | 
						|
  return Res;
 | 
						|
}
 | 
						|
 | 
						|
void TwEdwardsCurve::double_point(SegrePoint& Res, const SegrePoint& P) const {
 | 
						|
  add_points(Res, P, P);
 | 
						|
}
 | 
						|
 | 
						|
TwEdwardsCurve::SegrePoint TwEdwardsCurve::double_point(const SegrePoint& P) const {
 | 
						|
  SegrePoint Res(ring);
 | 
						|
  double_point(Res, P);
 | 
						|
  return Res;
 | 
						|
}
 | 
						|
 | 
						|
// computes u([n]P) in form (xy,x,y,1)*Z
 | 
						|
TwEdwardsCurve::SegrePoint TwEdwardsCurve::power_point(const SegrePoint& A, const Bignum& n) const {
 | 
						|
  assert(n >= 0);
 | 
						|
  if (n == 0) {
 | 
						|
    return O;
 | 
						|
  }
 | 
						|
 | 
						|
  int k = n.num_bits();
 | 
						|
  SegrePoint P(A);
 | 
						|
  SegrePoint Q(double_point(A));
 | 
						|
  for (int i = k - 2; i >= 0; --i) {
 | 
						|
    if (n[i]) {
 | 
						|
      add_points(P, P, Q);
 | 
						|
      double_point(Q, Q);
 | 
						|
    } else {
 | 
						|
      // we do more operations than necessary for uniformicity
 | 
						|
      add_points(Q, P, Q);
 | 
						|
      double_point(P, P);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return P;
 | 
						|
}
 | 
						|
 | 
						|
TwEdwardsCurve::SegrePoint TwEdwardsCurve::power_gen(const Bignum& n) const {
 | 
						|
  return power_point(G, n);
 | 
						|
}
 | 
						|
 | 
						|
bool TwEdwardsCurve::SegrePoint::export_point(unsigned char buffer[32], bool need_x) const {
 | 
						|
  if (!is_normalized()) {
 | 
						|
    if (Z.is_zero()) {
 | 
						|
      std::memset(buffer, 0xff, 32);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    Residue f(inverse(Z));
 | 
						|
    Bignum y((Y * f).extract());
 | 
						|
    assert(!y[255]);
 | 
						|
    if (need_x) {
 | 
						|
      y[255] = (X * f).extract().odd();
 | 
						|
    }
 | 
						|
    y.export_lsb(buffer, 32);
 | 
						|
  } else {
 | 
						|
    Bignum y(Y.extract());
 | 
						|
    assert(!y[255]);
 | 
						|
    if (need_x) {
 | 
						|
      y[255] = X.extract().odd();
 | 
						|
    }
 | 
						|
    y.export_lsb(buffer, 32);
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool TwEdwardsCurve::SegrePoint::export_point_u(unsigned char buffer[32]) const {
 | 
						|
  if (Z == Y) {
 | 
						|
    std::memset(buffer, 0xff, 32);
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  Residue f(inverse(Z - Y));
 | 
						|
  ((Z + Y) * f).extract().export_lsb(buffer, 32);
 | 
						|
  assert(!(buffer[31] & 0x80));
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
TwEdwardsCurve::SegrePoint TwEdwardsCurve::import_point(const unsigned char point[32], bool& ok) const {
 | 
						|
  Bignum y;
 | 
						|
  y.import_lsb(point, 32);
 | 
						|
  bool x_sign = y[255];
 | 
						|
  y[255] = 0;
 | 
						|
  Residue yr(y, ring);
 | 
						|
  Residue xr(ring);
 | 
						|
  ok = recover_x(xr, yr, x_sign);
 | 
						|
  return ok ? SegrePoint(xr, yr) : SegrePoint(ring);
 | 
						|
}
 | 
						|
 | 
						|
TwEdwardsCurve& Ed25519() {
 | 
						|
  static TwEdwardsCurve Ed25519(Fp25519().frac(-121665, 121666), Fp25519().frac(4, 5), Fp25519());
 | 
						|
  static bool init = false;
 | 
						|
  if (!init) {
 | 
						|
    Ed25519.set_order_cofactor(hex_string{"80000000000000000000000000000000a6f7cef517bce6b2c09318d2e7ae9f68"}, 8);
 | 
						|
    init = true;
 | 
						|
  }
 | 
						|
  return Ed25519;
 | 
						|
}
 | 
						|
}  // namespace ellcurve
 | 
						|
 | 
						|
// ******************************************************
 | 
						|
 | 
						|
namespace openssl {
 | 
						|
#include <openssl/evp.h>
 | 
						|
}
 | 
						|
 | 
						|
namespace digest {
 | 
						|
using namespace openssl;
 | 
						|
 | 
						|
struct OpensslEVP_SHA1 {
 | 
						|
  enum { digest_bytes = 20 };
 | 
						|
  static const EVP_MD* get_evp() {
 | 
						|
    return EVP_sha1();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
struct OpensslEVP_SHA256 {
 | 
						|
  enum { digest_bytes = 32 };
 | 
						|
  static const EVP_MD* get_evp() {
 | 
						|
    return EVP_sha256();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
struct OpensslEVP_SHA512 {
 | 
						|
  enum { digest_bytes = 64 };
 | 
						|
  static const EVP_MD* get_evp() {
 | 
						|
    return EVP_sha512();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
template <typename H>
 | 
						|
class HashCtx {
 | 
						|
  EVP_MD_CTX ctx;
 | 
						|
  void init();
 | 
						|
  void clear();
 | 
						|
 | 
						|
 public:
 | 
						|
  enum { digest_bytes = H::digest_bytes };
 | 
						|
  HashCtx() {
 | 
						|
    init();
 | 
						|
  }
 | 
						|
  HashCtx(const void* data, std::size_t len) {
 | 
						|
    init();
 | 
						|
    feed(data, len);
 | 
						|
  }
 | 
						|
  ~HashCtx() {
 | 
						|
    clear();
 | 
						|
  }
 | 
						|
  void feed(const void* data, std::size_t len);
 | 
						|
  std::size_t extract(unsigned char buffer[digest_bytes]);
 | 
						|
  std::string extract();
 | 
						|
};
 | 
						|
 | 
						|
template <typename H>
 | 
						|
void HashCtx<H>::init() {
 | 
						|
  EVP_MD_CTX_init(&ctx);
 | 
						|
  EVP_DigestInit_ex(&ctx, H::get_evp(), 0);
 | 
						|
}
 | 
						|
 | 
						|
template <typename H>
 | 
						|
void HashCtx<H>::clear() {
 | 
						|
  EVP_MD_CTX_cleanup(&ctx);
 | 
						|
}
 | 
						|
 | 
						|
template <typename H>
 | 
						|
void HashCtx<H>::feed(const void* data, std::size_t len) {
 | 
						|
  EVP_DigestUpdate(&ctx, data, len);
 | 
						|
}
 | 
						|
 | 
						|
template <typename H>
 | 
						|
std::size_t HashCtx<H>::extract(unsigned char buffer[digest_bytes]) {
 | 
						|
  unsigned olen = 0;
 | 
						|
  EVP_DigestFinal_ex(&ctx, buffer, &olen);
 | 
						|
  assert(olen == digest_bytes);
 | 
						|
  return olen;
 | 
						|
}
 | 
						|
 | 
						|
template <typename H>
 | 
						|
std::string HashCtx<H>::extract() {
 | 
						|
  unsigned char buffer[digest_bytes];
 | 
						|
  unsigned olen = 0;
 | 
						|
  EVP_DigestFinal_ex(&ctx, buffer, &olen);
 | 
						|
  assert(olen == digest_bytes);
 | 
						|
  return std::string((char*)buffer, olen);
 | 
						|
}
 | 
						|
 | 
						|
typedef HashCtx<OpensslEVP_SHA1> SHA1;
 | 
						|
typedef HashCtx<OpensslEVP_SHA256> SHA256;
 | 
						|
typedef HashCtx<OpensslEVP_SHA512> SHA512;
 | 
						|
 | 
						|
template <typename T>
 | 
						|
std::size_t hash_str(unsigned char buffer[T::digest_bytes], const void* data, std::size_t size) {
 | 
						|
  T hasher(data, size);
 | 
						|
  return hasher.extract(buffer);
 | 
						|
}
 | 
						|
 | 
						|
template <typename T>
 | 
						|
std::size_t hash_two_str(unsigned char buffer[T::digest_bytes], const void* data1, std::size_t size1, const void* data2,
 | 
						|
                         std::size_t size2) {
 | 
						|
  T hasher(data1, size1);
 | 
						|
  hasher.feed(data2, size2);
 | 
						|
  return hasher.extract(buffer);
 | 
						|
}
 | 
						|
 | 
						|
template <typename T>
 | 
						|
std::string hash_str(const void* data, std::size_t size) {
 | 
						|
  T hasher(data, size);
 | 
						|
  return hasher.extract();
 | 
						|
}
 | 
						|
 | 
						|
template <typename T>
 | 
						|
std::string hash_two_str(const void* data1, std::size_t size1, const void* data2, std::size_t size2) {
 | 
						|
  T hasher(data1, size1);
 | 
						|
  hasher.feed(data2, size2);
 | 
						|
  return hasher.extract();
 | 
						|
}
 | 
						|
}  // namespace digest
 | 
						|
 | 
						|
// ******************************************************
 | 
						|
 | 
						|
namespace openssl {
 | 
						|
#include <openssl/rand.h>
 | 
						|
}
 | 
						|
 | 
						|
#include <fcntl.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
namespace prng {
 | 
						|
 | 
						|
int os_get_random_bytes(void* buf, int n) {
 | 
						|
  using namespace std;
 | 
						|
  int r = 0, h = open("/dev/random", O_RDONLY | O_NONBLOCK);
 | 
						|
  if (h >= 0) {
 | 
						|
    r = read(h, buf, n);
 | 
						|
    if (r > 0) {
 | 
						|
      //std::cerr << "added " << r << " bytes of real entropy to secure random numbers seed" << std::endl;
 | 
						|
    } else {
 | 
						|
      r = 0;
 | 
						|
    }
 | 
						|
    close(h);
 | 
						|
  }
 | 
						|
 | 
						|
  if (r < n) {
 | 
						|
    h = open("/dev/urandom", O_RDONLY);
 | 
						|
    if (h < 0) {
 | 
						|
      return r;
 | 
						|
    }
 | 
						|
    int s = read(h, (char*)buf + r, n - r);
 | 
						|
    close(h);
 | 
						|
    if (s < 0) {
 | 
						|
      return r;
 | 
						|
    }
 | 
						|
    r += s;
 | 
						|
  }
 | 
						|
 | 
						|
  if (r >= 8) {
 | 
						|
    *(long*)buf ^= lrand48();
 | 
						|
    srand48(*(long*)buf);
 | 
						|
  }
 | 
						|
 | 
						|
  return r;
 | 
						|
}
 | 
						|
}  // namespace prng
 | 
						|
 | 
						|
namespace prng {
 | 
						|
using namespace openssl;
 | 
						|
 | 
						|
class RandomGen {
 | 
						|
 public:
 | 
						|
  struct rand_error {};
 | 
						|
  void randomize(bool force = true);
 | 
						|
  void seed_add(const void* data, std::size_t size, double entropy = 0);
 | 
						|
  bool ok() const {
 | 
						|
    return RAND_status();
 | 
						|
  }
 | 
						|
  RandomGen() {
 | 
						|
    randomize(false);
 | 
						|
  }
 | 
						|
  RandomGen(const void* seed, std::size_t size) {
 | 
						|
    seed_add(seed, size);
 | 
						|
    randomize(false);
 | 
						|
  }
 | 
						|
  bool rand_bytes(void* data, std::size_t size, bool strong = false);
 | 
						|
  bool strong_rand_bytes(void* data, std::size_t size) {
 | 
						|
    return rand_bytes(data, size, true);
 | 
						|
  }
 | 
						|
  template <class T>
 | 
						|
  bool rand_obj(T& obj) {
 | 
						|
    return rand_bytes(&obj, sizeof(T));
 | 
						|
  }
 | 
						|
  template <class T>
 | 
						|
  bool rand_objs(T* ptr, std::size_t count) {
 | 
						|
    return rand_bytes(ptr, sizeof(T) * count);
 | 
						|
  }
 | 
						|
  std::string rand_string(std::size_t size, bool strong = false);
 | 
						|
};
 | 
						|
 | 
						|
void RandomGen::seed_add(const void* data, std::size_t size, double entropy) {
 | 
						|
  RAND_add(data, size, entropy > 0 ? entropy : size);
 | 
						|
}
 | 
						|
 | 
						|
void RandomGen::randomize(bool force) {
 | 
						|
  if (!force && ok()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  unsigned char buffer[128];
 | 
						|
  int n = os_get_random_bytes(buffer, 128);
 | 
						|
  seed_add(buffer, n);
 | 
						|
  assert(ok());
 | 
						|
}
 | 
						|
 | 
						|
bool RandomGen::rand_bytes(void* data, std::size_t size, bool strong) {
 | 
						|
  int res = (strong ? RAND_bytes : RAND_pseudo_bytes)((unsigned char*)data, size);
 | 
						|
  if (res != 0 && res != 1) {
 | 
						|
    throw rand_error();
 | 
						|
  }
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
std::string RandomGen::rand_string(std::size_t size, bool strong) {
 | 
						|
  char buffer[size];
 | 
						|
  if (!rand_bytes(buffer, size, strong)) {
 | 
						|
    throw rand_error();
 | 
						|
  }
 | 
						|
  return std::string(buffer, size);
 | 
						|
}
 | 
						|
 | 
						|
RandomGen& rand_gen() {
 | 
						|
  static RandomGen MainPRNG;
 | 
						|
  return MainPRNG;
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace prng
 | 
						|
 | 
						|
// ******************************************************
 | 
						|
 | 
						|
namespace crypto {
 | 
						|
namespace Ed25519 {
 | 
						|
 | 
						|
const int privkey_bytes = 32;
 | 
						|
const int pubkey_bytes = 32;
 | 
						|
const int sign_bytes = 64;
 | 
						|
const int shared_secret_bytes = 32;
 | 
						|
 | 
						|
bool all_bytes_same(const unsigned char* str, std::size_t size) {
 | 
						|
  unsigned char c = str[0];
 | 
						|
  for (std::size_t i = 0; i < size; i++) {
 | 
						|
    if (str[i] != c) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
class PublicKey {
 | 
						|
  enum { pk_empty, pk_xz, pk_init } inited;
 | 
						|
  unsigned char pubkey[pubkey_bytes];
 | 
						|
  ellcurve::TwEdwardsCurve::SegrePoint PubKey;
 | 
						|
  ellcurve::MontgomeryCurve::PointXZ PubKey_xz;
 | 
						|
 | 
						|
 public:
 | 
						|
  PublicKey() : inited(pk_empty), PubKey(ellcurve::Fp25519()), PubKey_xz(ellcurve::Fp25519()) {
 | 
						|
  }
 | 
						|
  PublicKey(const unsigned char pub_key[pubkey_bytes]);
 | 
						|
  PublicKey(const ellcurve::TwEdwardsCurve::SegrePoint& Pub_Key);
 | 
						|
 | 
						|
  bool import_public_key(const unsigned char pub_key[pubkey_bytes]);
 | 
						|
  bool import_public_key(const ellcurve::TwEdwardsCurve::SegrePoint& Pub_Key);
 | 
						|
  bool export_public_key(unsigned char pubkey_buffer[pubkey_bytes]) const;
 | 
						|
  bool check_message_signature(unsigned char signature[sign_bytes], const unsigned char* message, std::size_t msg_size);
 | 
						|
 | 
						|
  void clear();
 | 
						|
  bool ok() const {
 | 
						|
    return inited == pk_init;
 | 
						|
  }
 | 
						|
 | 
						|
  const unsigned char* get_pubkey_ptr() const {
 | 
						|
    return inited == pk_init ? pubkey : 0;
 | 
						|
  }
 | 
						|
  const ellcurve::TwEdwardsCurve::SegrePoint& get_point() const {
 | 
						|
    return PubKey;
 | 
						|
  }
 | 
						|
  const ellcurve::MontgomeryCurve::PointXZ& get_point_xz() const {
 | 
						|
    return PubKey_xz;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
void PublicKey::clear(void) {
 | 
						|
  if (inited != pk_empty) {
 | 
						|
    std::memset(pubkey, 0, pubkey_bytes);
 | 
						|
    PubKey.zeroize();
 | 
						|
    PubKey_xz.zeroize();
 | 
						|
  }
 | 
						|
  inited = pk_empty;
 | 
						|
}
 | 
						|
 | 
						|
PublicKey::PublicKey(const unsigned char pub_key[pubkey_bytes])
 | 
						|
    : inited(pk_empty), PubKey(ellcurve::Fp25519()), PubKey_xz(ellcurve::Fp25519()) {
 | 
						|
  import_public_key(pub_key);
 | 
						|
}
 | 
						|
 | 
						|
PublicKey::PublicKey(const ellcurve::TwEdwardsCurve::SegrePoint& Pub_Key)
 | 
						|
    : inited(pk_empty), PubKey(ellcurve::Fp25519()), PubKey_xz(ellcurve::Fp25519()) {
 | 
						|
  import_public_key(Pub_Key);
 | 
						|
}
 | 
						|
 | 
						|
bool PublicKey::import_public_key(const unsigned char pub_key[pubkey_bytes]) {
 | 
						|
  clear();
 | 
						|
  if (all_bytes_same(pub_key, pubkey_bytes)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  bool ok = false;
 | 
						|
  PubKey = ellcurve::Ed25519().import_point(pub_key, ok);
 | 
						|
  if (!ok) {
 | 
						|
    clear();
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  std::memcpy(pubkey, pub_key, pubkey_bytes);
 | 
						|
  PubKey_xz.X = PubKey.Z + PubKey.Y;
 | 
						|
  PubKey_xz.Z = PubKey.Z - PubKey.Y;
 | 
						|
  inited = pk_init;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool PublicKey::import_public_key(const ellcurve::TwEdwardsCurve::SegrePoint& Pub_Key) {
 | 
						|
  clear();
 | 
						|
  if (!Pub_Key.is_valid()) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  PubKey = Pub_Key;
 | 
						|
  PubKey_xz.X = PubKey.Z + PubKey.Y;
 | 
						|
  PubKey_xz.Z = PubKey.Z - PubKey.Y;
 | 
						|
  inited = pk_init;
 | 
						|
 | 
						|
  if (!PubKey.export_point(pubkey)) {
 | 
						|
    clear();
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool PublicKey::export_public_key(unsigned char pubkey_buffer[pubkey_bytes]) const {
 | 
						|
  if (inited != pk_init) {
 | 
						|
    std::memset(pubkey_buffer, 0, pubkey_bytes);
 | 
						|
    return false;
 | 
						|
  } else {
 | 
						|
    std::memcpy(pubkey_buffer, pubkey, pubkey_bytes);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool PublicKey::check_message_signature(unsigned char signature[sign_bytes], const unsigned char* message,
 | 
						|
                                        std::size_t msg_size) {
 | 
						|
  if (inited != pk_init) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  unsigned char hash[64];
 | 
						|
  {
 | 
						|
    digest::SHA512 hasher(signature, 32);
 | 
						|
    hasher.feed(pubkey, 32);
 | 
						|
    hasher.feed(message, msg_size);
 | 
						|
    hasher.extract(hash);
 | 
						|
  }
 | 
						|
  auto& E = ellcurve::Ed25519();
 | 
						|
  const arith::Bignum& L = E.get_ell();
 | 
						|
  arith::Bignum H, S;
 | 
						|
  S.import_lsb(signature + 32, 32);
 | 
						|
  H.import_lsb(hash, 64);
 | 
						|
  H %= L;
 | 
						|
  H = L - H;
 | 
						|
  auto sG = E.power_gen(S);
 | 
						|
  auto hA = E.power_point(PubKey, H);
 | 
						|
  auto pR1 = E.add_points(sG, hA);
 | 
						|
  unsigned char pR1_bytes[32];
 | 
						|
  if (!pR1.export_point(pR1_bytes)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  return !std::memcmp(pR1_bytes, signature, 32);
 | 
						|
}
 | 
						|
 | 
						|
class PrivateKey {
 | 
						|
 public:
 | 
						|
  struct priv_key_no_copy {};
 | 
						|
  PrivateKey() : inited(false) {
 | 
						|
    std::memset(privkey, 0, privkey_bytes);
 | 
						|
  }
 | 
						|
  PrivateKey(const unsigned char pk[privkey_bytes]) : inited(false) {
 | 
						|
    std::memset(privkey, 0, privkey_bytes);
 | 
						|
    import_private_key(pk);
 | 
						|
  }
 | 
						|
  ~PrivateKey() {
 | 
						|
    clear();
 | 
						|
  }
 | 
						|
  bool random_private_key(bool strong = false);
 | 
						|
  bool import_private_key(const unsigned char pk[privkey_bytes]);
 | 
						|
  bool export_private_key(unsigned char pk[privkey_bytes]) const;  // careful!
 | 
						|
  bool export_public_key(unsigned char pubk[pubkey_bytes]) const {
 | 
						|
    return PubKey.export_public_key(pubk);
 | 
						|
  }
 | 
						|
  void clear();
 | 
						|
  bool ok() const {
 | 
						|
    return inited;
 | 
						|
  }
 | 
						|
 | 
						|
  // used for EdDSA (sign)
 | 
						|
  bool sign_message(unsigned char signature[sign_bytes], const unsigned char* message, std::size_t msg_size);
 | 
						|
  // used for ECDH (encrypt / decrypt)
 | 
						|
  bool compute_shared_secret(unsigned char secret[shared_secret_bytes], const PublicKey& Pub);
 | 
						|
  // used for EC asymmetric decryption
 | 
						|
  bool compute_temp_shared_secret(unsigned char secret[shared_secret_bytes],
 | 
						|
                                  const unsigned char temp_pub_key[pubkey_bytes]);
 | 
						|
 | 
						|
  const PublicKey& get_public_key() const {
 | 
						|
    return PubKey;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  bool inited;
 | 
						|
  unsigned char privkey[privkey_bytes];
 | 
						|
  unsigned char priv_salt[32];
 | 
						|
  arith::Bignum priv_exp;
 | 
						|
  PublicKey PubKey;
 | 
						|
 | 
						|
  bool process_private_key();
 | 
						|
  PrivateKey(const PrivateKey&) {
 | 
						|
    throw priv_key_no_copy();
 | 
						|
  }
 | 
						|
  PrivateKey& operator=(const PrivateKey&) {
 | 
						|
    throw priv_key_no_copy();
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
bool PrivateKey::random_private_key(bool strong) {
 | 
						|
  inited = false;
 | 
						|
  if (!prng::rand_gen().rand_bytes(privkey, privkey_bytes, strong)) {
 | 
						|
    clear();
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  return process_private_key();
 | 
						|
}
 | 
						|
 | 
						|
void PrivateKey::clear(void) {
 | 
						|
  std::memset(privkey, 0, privkey_bytes);
 | 
						|
  std::memset(priv_salt, 0, sizeof(priv_salt));
 | 
						|
  priv_exp.clear();
 | 
						|
  PubKey.clear();
 | 
						|
  inited = false;
 | 
						|
}
 | 
						|
 | 
						|
bool PrivateKey::import_private_key(const unsigned char pk[privkey_bytes]) {
 | 
						|
  clear();
 | 
						|
  if (all_bytes_same(pk, privkey_bytes)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  std::memcpy(privkey, pk, privkey_bytes);
 | 
						|
  return process_private_key();
 | 
						|
}
 | 
						|
 | 
						|
bool PrivateKey::export_private_key(unsigned char pk[privkey_bytes]) const {  // careful!
 | 
						|
  if (!inited) {
 | 
						|
    std::memset(pk, 0, privkey_bytes);
 | 
						|
    return false;
 | 
						|
  } else {
 | 
						|
    std::memcpy(pk, privkey, privkey_bytes);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool PrivateKey::process_private_key() {
 | 
						|
  unsigned char buff[64];
 | 
						|
  digest::hash_str<digest::SHA512>(buff, privkey, privkey_bytes);
 | 
						|
  std::memcpy(priv_salt, buff + 32, 32);
 | 
						|
  buff[0] &= -8;
 | 
						|
  buff[31] = ((buff[31] | 0x40) & ~0x80);
 | 
						|
  priv_exp.import_lsb(buff, 32);
 | 
						|
  PubKey = ellcurve::Ed25519().power_gen(priv_exp);
 | 
						|
  inited = PubKey.ok();
 | 
						|
  if (!inited) {
 | 
						|
    clear();
 | 
						|
  }
 | 
						|
  return inited;
 | 
						|
}
 | 
						|
 | 
						|
bool PrivateKey::compute_shared_secret(unsigned char secret[shared_secret_bytes], const PublicKey& Pub) {
 | 
						|
  if (!inited || !Pub.ok()) {
 | 
						|
    std::memset(secret, 0, shared_secret_bytes);
 | 
						|
    *(long*)secret = lrand48();
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  auto P = ellcurve::Curve25519().power_xz(Pub.get_point_xz(), priv_exp);
 | 
						|
  if (P.is_infty()) {
 | 
						|
    std::memset(secret, 0, shared_secret_bytes);
 | 
						|
    *(long*)secret = lrand48();
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  P.export_point_y(secret);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool PrivateKey::compute_temp_shared_secret(unsigned char secret[shared_secret_bytes],
 | 
						|
                                            const unsigned char temp_pub_key[pubkey_bytes]) {
 | 
						|
  PublicKey tempPubkey(temp_pub_key);
 | 
						|
  if (!tempPubkey.ok()) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  return compute_shared_secret(secret, tempPubkey);
 | 
						|
}
 | 
						|
 | 
						|
bool PrivateKey::sign_message(unsigned char signature[sign_bytes], const unsigned char* message, std::size_t msg_size) {
 | 
						|
  if (!inited) {
 | 
						|
    std::memset(signature, 0, sign_bytes);
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  unsigned char r_bytes[64];
 | 
						|
  digest::hash_two_str<digest::SHA512>(r_bytes, priv_salt, 32, message, msg_size);
 | 
						|
  const arith::Bignum& L = ellcurve::Ed25519().get_ell();
 | 
						|
  arith::Bignum eR;
 | 
						|
  eR.import_lsb(r_bytes, 64);
 | 
						|
  eR %= L;
 | 
						|
 | 
						|
  auto pR = ellcurve::Ed25519().power_gen(eR);
 | 
						|
 | 
						|
  assert(pR.export_point(signature, 32));
 | 
						|
  {
 | 
						|
    digest::SHA512 hasher(signature, 32);
 | 
						|
    hasher.feed(PubKey.get_pubkey_ptr(), 32);
 | 
						|
    hasher.feed(message, msg_size);
 | 
						|
    hasher.extract(r_bytes);
 | 
						|
  }
 | 
						|
  arith::Bignum S;
 | 
						|
  S.import_lsb(r_bytes, 64);
 | 
						|
  S %= L;
 | 
						|
  S *= priv_exp;
 | 
						|
  S += eR;
 | 
						|
  S %= L;
 | 
						|
  S.export_lsb(signature + 32, 32);
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
// use one TempKeyGenerator object a lot of times
 | 
						|
class TempKeyGenerator {
 | 
						|
  enum { salt_size = 64 };
 | 
						|
  unsigned char random_salt[salt_size];
 | 
						|
  unsigned char buffer[privkey_bytes];
 | 
						|
 | 
						|
 public:
 | 
						|
  TempKeyGenerator() {
 | 
						|
    prng::rand_gen().strong_rand_bytes(random_salt, salt_size);
 | 
						|
  }
 | 
						|
  ~TempKeyGenerator() {
 | 
						|
    std::memset(random_salt, 0, salt_size);
 | 
						|
    std::memset(buffer, 0, privkey_bytes);
 | 
						|
  }
 | 
						|
 | 
						|
  unsigned char* get_temp_private_key(unsigned char* to, const unsigned char* message, std::size_t size,
 | 
						|
                                      const unsigned char* rand = 0, std::size_t rand_size = 0);  // rand may be 0
 | 
						|
  void create_temp_private_key(PrivateKey& pk, const unsigned char* message, std::size_t size,
 | 
						|
                               const unsigned char* rand = 0, std::size_t rand_size = 0);
 | 
						|
 | 
						|
  // sets temp_pub_key and shared_secret for one-time asymmetric encryption of message
 | 
						|
  bool create_temp_shared_secret(unsigned char temp_pub_key[pubkey_bytes], unsigned char secret[shared_secret_bytes],
 | 
						|
                                 const PublicKey& recipientPubKey, const unsigned char* message, std::size_t size,
 | 
						|
                                 const unsigned char* rand = 0, std::size_t rand_size = 0);
 | 
						|
};
 | 
						|
 | 
						|
unsigned char* TempKeyGenerator::get_temp_private_key(unsigned char* to, const unsigned char* message, std::size_t size,
 | 
						|
                                                      const unsigned char* rand,
 | 
						|
                                                      std::size_t rand_size) {  // rand may be 0
 | 
						|
  digest::SHA256 hasher(message, size);
 | 
						|
  hasher.feed(random_salt, salt_size);
 | 
						|
  if (rand && rand_size) {
 | 
						|
    hasher.feed(rand, rand_size);
 | 
						|
  }
 | 
						|
  if (!to) {
 | 
						|
    to = buffer;
 | 
						|
  }
 | 
						|
  hasher.extract(to);
 | 
						|
  //++ *((long *)random_salt);
 | 
						|
  return to;
 | 
						|
}
 | 
						|
 | 
						|
void TempKeyGenerator::create_temp_private_key(PrivateKey& pk, const unsigned char* message, std::size_t size,
 | 
						|
                                               const unsigned char* rand, std::size_t rand_size) {
 | 
						|
  pk.import_private_key(get_temp_private_key(buffer, message, size, rand, rand_size));
 | 
						|
  std::memset(buffer, 0, privkey_bytes);
 | 
						|
}
 | 
						|
 | 
						|
bool TempKeyGenerator::create_temp_shared_secret(unsigned char temp_pub_key[pubkey_bytes],
 | 
						|
                                                 unsigned char shared_secret[shared_secret_bytes],
 | 
						|
                                                 const PublicKey& recipientPubKey, const unsigned char* message,
 | 
						|
                                                 std::size_t size, const unsigned char* rand, std::size_t rand_size) {
 | 
						|
  PrivateKey tmpPk;
 | 
						|
  create_temp_private_key(tmpPk, message, size, rand, rand_size);
 | 
						|
  return tmpPk.export_public_key(temp_pub_key) && tmpPk.compute_shared_secret(shared_secret, recipientPubKey);
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace Ed25519
 | 
						|
}  // namespace crypto
 | 
						|
 | 
						|
// ******************************************************
 | 
						|
 | 
						|
void print_buffer(const unsigned char buffer[32]) {
 | 
						|
  for (int i = 0; i < 32; i++) {
 | 
						|
    char buff[4];
 | 
						|
    sprintf(buff, "%02x", buffer[i]);
 | 
						|
    std::cout << buff;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
std::string buffer_to_hex(const unsigned char* buffer, std::size_t size = 32) {
 | 
						|
  char str[2 * size + 1];
 | 
						|
  for (std::size_t i = 0; i < size; i++) {
 | 
						|
    sprintf(str + 2 * i, "%02x", buffer[i]);
 | 
						|
  }
 | 
						|
  return str;
 | 
						|
}
 | 
						|
 | 
						|
int main(void) {
 | 
						|
  arith::Bignum x = (3506824292LL << 31);
 | 
						|
  x = (2948877059LL << 31);
 | 
						|
  arith::Bignum L = (((36 * x + 36) * x + 18) * x + 6) * x + 1;
 | 
						|
  arith::Bignum P = L + 6 * sqr(x);
 | 
						|
  std::cout << "x= " << x << "; l= " << L << "; p= " << P << std::endl;
 | 
						|
  std::cout << "x= " << x.to_hex() << "; l= " << L.to_hex() << "; p= " << P.to_hex() << std::endl;
 | 
						|
  std::cout << "x mod 3=" << x % 3 << "; p mod 9=" << P % 9 << "; x/2^31=" << (x >> 31).to_hex() << "=" << (x >> 31)
 | 
						|
            << std::endl;
 | 
						|
 | 
						|
  crypto::Ed25519::PrivateKey PK1, PK2, PK3;
 | 
						|
  PK1.random_private_key();
 | 
						|
  PK2.random_private_key();
 | 
						|
  unsigned char priv2_export[32];
 | 
						|
  bool ok = PK2.export_private_key(priv2_export);
 | 
						|
  std::cout << "PK2 = " << ok << " " << buffer_to_hex(priv2_export) << std::endl;
 | 
						|
  PK3.import_private_key(priv2_export);
 | 
						|
  std::cout << "PK3 = " << PK3.ok() << std::endl;
 | 
						|
 | 
						|
  unsigned char pub_export[32];
 | 
						|
  ok = PK1.export_public_key(pub_export);
 | 
						|
  std::cout << "PubK1 = " << ok << " " << buffer_to_hex(pub_export) << std::endl;
 | 
						|
  crypto::Ed25519::PublicKey PubK1(pub_export);
 | 
						|
  ok = PK2.export_public_key(pub_export);
 | 
						|
  std::cout << "PubK2 = " << ok << " " << buffer_to_hex(pub_export) << std::endl;
 | 
						|
  crypto::Ed25519::PublicKey PubK2(pub_export);
 | 
						|
  ok = PK3.export_public_key(pub_export);
 | 
						|
  std::cout << "PubK3 = " << ok << " " << buffer_to_hex(pub_export) << std::endl;
 | 
						|
  crypto::Ed25519::PublicKey PubK3(pub_export);
 | 
						|
  ok = PubK1.export_public_key(pub_export);
 | 
						|
  std::cout << "PubK1 = " << ok << " " << buffer_to_hex(pub_export) << std::endl;
 | 
						|
 | 
						|
  unsigned char secret12[32], secret21[32];
 | 
						|
  ok = PK1.compute_shared_secret(secret12, PK3.get_public_key());
 | 
						|
  std::cout << "secret(PK1,PubK2)=" << ok << " " << buffer_to_hex(secret12) << std::endl;
 | 
						|
  ok = PK2.compute_shared_secret(secret21, PubK1);
 | 
						|
  std::cout << "secret(PK2,PubK1)=" << ok << " " << buffer_to_hex(secret21) << std::endl;
 | 
						|
 | 
						|
  unsigned char signature[64];
 | 
						|
  ok = PK1.sign_message(signature, (const unsigned char*)"abc", 3);
 | 
						|
  std::cout << "PK1.signature=" << ok << " " << buffer_to_hex(signature) << std::endl;
 | 
						|
 | 
						|
  // signature[63] ^= 1;
 | 
						|
  ok = PubK1.check_message_signature(signature, (const unsigned char*)"abc", 3);
 | 
						|
  std::cout << "PubK1.check_signature=" << ok << std::endl;
 | 
						|
 | 
						|
  unsigned char temp_pubkey[32];
 | 
						|
  crypto::Ed25519::TempKeyGenerator TKG;  // use one generator a lot of times
 | 
						|
 | 
						|
  TKG.create_temp_shared_secret(temp_pubkey, secret12, PubK1, (const unsigned char*)"abc", 3);
 | 
						|
  std::cout << "secret12=" << buffer_to_hex(secret12) << "; temp_pubkey=" << buffer_to_hex(temp_pubkey) << std::endl;
 | 
						|
 | 
						|
  PK1.compute_temp_shared_secret(secret21, temp_pubkey);
 | 
						|
  std::cout << "secret21=" << buffer_to_hex(secret21) << std::endl;
 | 
						|
}
 |