1
0
mirror of https://github.com/danog/ton.git synced 2024-11-26 20:14:55 +01:00
ton/test/ed25519_crypto.cpp
2019-09-07 14:33:36 +04:00

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