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;
 | |
| }
 |