1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

initial commit

This commit is contained in:
initial commit 2019-09-07 14:03:22 +04:00 committed by vvaltman
commit c2da007f40
1610 changed files with 398047 additions and 0 deletions

280
crypto/ellcurve/Ed25519.cpp Normal file
View file

@ -0,0 +1,280 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "Ed25519.h"
#include "td/utils/Random.h"
namespace crypto {
namespace Ed25519 {
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;
}
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(const 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;
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] = (unsigned char)(buff[0] & -8);
buff[31] = (unsigned char)((buff[31] | 0x40) & ~0x80);
priv_exp.import_lsb(buff, 32);
PubKey = ellcurve::Ed25519().power_gen(priv_exp, true); // uniform
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 = static_cast<long>(td::Random::fast_uint64());
return false;
}
// uniform power!
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 = static_cast<long>(td::Random::fast_uint64());
return false;
}
P.export_point_u(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;
std::memset(r_bytes, 0, sizeof(r_bytes));
// uniform power
auto pR = ellcurve::Ed25519().power_gen(eR, true);
auto ok = pR.export_point(signature, true);
(void)ok;
assert(ok);
{
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;
eR.clear();
S.export_lsb(signature + 32, 32);
return true;
}
// ---------------------------------
class TempKeyGenerator;
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

188
crypto/ellcurve/Ed25519.h Normal file
View file

@ -0,0 +1,188 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "ellcurve/Montgomery.h"
#include "ellcurve/TwEdwards.h"
#include "openssl/digest.h"
#include "openssl/rand.hpp"
#include <assert.h>
#include <cstring>
#include "td/utils/buffer.h"
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;
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(td::Slice pub_key) : PublicKey(pub_key.ubegin()) {
CHECK(pub_key.size() == pubkey_bytes);
}
PublicKey(const ellcurve::TwEdwardsCurve::SegrePoint &Pub_Key);
bool import_public_key(const unsigned char pub_key[pubkey_bytes]);
bool import_public_key(td::Slice pub_key) {
CHECK(pub_key.size() == pubkey_bytes);
return import_public_key(pub_key.ubegin());
}
bool import_public_key(const ellcurve::TwEdwardsCurve::SegrePoint &Pub_Key);
bool export_public_key(unsigned char pubkey_buffer[pubkey_bytes]) const;
bool export_public_key(td::MutableSlice pubk) const {
CHECK(pubk.size() == pubkey_bytes);
return export_public_key(pubk.ubegin());
}
bool check_message_signature(const unsigned char signature[sign_bytes], const unsigned char *message,
std::size_t msg_size);
bool check_message_signature(td::Slice signature, td::Slice message) {
CHECK(signature.size() == sign_bytes);
return check_message_signature(signature.ubegin(), message.ubegin(), message.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;
}
};
class PrivateKey {
public:
struct priv_key_no_copy {};
PrivateKey() : inited(false) {
memset(privkey, 0, privkey_bytes);
}
PrivateKey(const unsigned char pk[privkey_bytes]) : inited(false) {
memset(privkey, 0, privkey_bytes);
import_private_key(pk);
}
PrivateKey(td::Slice pk) : inited(false) {
CHECK(pk.size() == privkey_bytes);
memset(privkey, 0, privkey_bytes);
import_private_key(pk.ubegin());
}
~PrivateKey() {
clear();
}
bool random_private_key(bool strong = false);
bool import_private_key(const unsigned char pk[privkey_bytes]);
bool import_private_key(td::Slice pk) {
CHECK(pk.size() == privkey_bytes);
return import_private_key(pk.ubegin());
}
bool export_private_key(unsigned char pk[privkey_bytes]) const; // careful!
bool export_private_key(td::MutableSlice pk) const { // careful!
return export_private_key(pk.ubegin());
}
bool export_public_key(unsigned char pubk[pubkey_bytes]) const {
return PubKey.export_public_key(pubk);
}
bool export_public_key(td::MutableSlice pubk) 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);
bool sign_message(td::MutableSlice signature, td::Slice message) {
CHECK(signature.size() == sign_bytes);
return sign_message(signature.ubegin(), message.ubegin(), message.size());
}
// used for ECDH (encrypt / decrypt)
bool compute_shared_secret(unsigned char secret[shared_secret_bytes], const PublicKey &Pub);
bool compute_shared_secret(td::MutableSlice secret, const PublicKey &Pub) {
CHECK(secret.size() == shared_secret_bytes);
return compute_shared_secret(secret.ubegin(), 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();
}
};
// 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() {
memset(random_salt, 0, salt_size);
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);
};
} // namespace Ed25519
} // namespace crypto

View file

@ -0,0 +1,33 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "ellcurve/Fp25519.h"
namespace ellcurve {
using namespace arith;
const Bignum& P25519() {
static const Bignum P25519 = (Bignum(1) << 255) - 19;
return P25519;
}
td::Ref<ResidueRing> Fp25519() {
static const td::Ref<ResidueRing> Fp25519(true, P25519());
return Fp25519;
}
} // namespace ellcurve

32
crypto/ellcurve/Fp25519.h Normal file
View file

@ -0,0 +1,32 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "common/refcnt.hpp"
#include "openssl/residue.h"
namespace ellcurve {
using namespace arith;
// returns 2^255-19
const Bignum& P25519();
// residue ring modulo P25519
td::Ref<ResidueRing> Fp25519();
} // namespace ellcurve

View file

@ -0,0 +1,138 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "ellcurve/Montgomery.h"
#include <assert.h>
#include <cstring>
namespace ellcurve {
using namespace arith;
class MontgomeryCurve;
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);
}
const MontgomeryCurve& Curve25519() {
static const MontgomeryCurve Curve25519 = [] {
MontgomeryCurve res(486662, 9, Fp25519());
res.set_order_cofactor(hex_string{"80000000000000000000000000000000a6f7cef517bce6b2c09318d2e7ae9f68"}, 8);
return res;
}();
return Curve25519;
}
} // namespace ellcurve

View file

@ -0,0 +1,123 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <iostream>
#include <string>
#include "openssl/bignum.h"
#include "openssl/residue.h"
#include "ellcurve/Fp25519.h"
namespace ellcurve {
using namespace arith;
class MontgomeryCurve {
td::Ref<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, td::Ref<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;
}
td::Ref<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(td::Ref<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.ring_of().one() - y) {
}
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;
};
const MontgomeryCurve& Curve25519();
} // namespace ellcurve

View file

@ -0,0 +1,255 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "ellcurve/TwEdwards.h"
#include <assert.h>
#include <cstring>
namespace ellcurve {
using namespace arith;
class TwEdwardsCurve;
TwEdwardsCurve::TwEdwardsCurve(const Residue& _D, const Residue& _Gy, td::Ref<ResidueRing> _R)
: ring(_R)
, D(_D)
, D2(_D + _D)
, Gy(_Gy)
, P_(_R->get_modulus())
, cofactor_short(0)
, G(_R)
, O(_R)
, table_lines(0)
, table() {
init();
}
TwEdwardsCurve::~TwEdwardsCurve() {
}
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_ref());
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, bool uniform) const {
assert(n >= 0);
if (n == 0) {
return O;
}
int k = n.num_bits();
SegrePoint P(A);
if (uniform) {
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);
}
}
} else {
for (int i = k - 2; i >= 0; --i) {
double_point(P, P);
if (n[i]) {
add_points(P, P, A); // may optimize further if A.z = 1
}
}
}
return P;
}
int TwEdwardsCurve::build_table() {
if (table.size()) {
return -1;
}
table_lines = (P_.num_bits() >> 2) + 2;
table.reserve(table_lines * 15 + 1);
table.emplace_back(get_base_point());
for (int i = 0; i < table_lines; i++) {
for (int j = 0; j < 15; j++) {
table.emplace_back(add_points(table[15 * i + j], table[15 * i]));
}
}
return 1;
}
int get_nibble(const Bignum& n, int idx) {
return n[idx * 4 + 3] * 8 + n[idx * 4 + 2] * 4 + n[idx * 4 + 1] * 2 + n[idx * 4];
}
TwEdwardsCurve::SegrePoint TwEdwardsCurve::power_gen(const Bignum& n, bool uniform) const {
if (uniform || n.num_bits() > table_lines * 4) {
return power_point(G, n, uniform);
} else if (n.is_zero()) {
return O;
} else {
int k = (n.num_bits() + 3) >> 2;
assert(k > 0 && k <= table_lines);
int x = get_nibble(n, k - 1);
assert(x > 0 && x < 16);
SegrePoint P(table[15 * (k - 1) + x - 1]);
for (int i = k - 2; i >= 0; i--) {
x = get_nibble(n, i);
assert(x >= 0 && x < 16);
if (x > 0) {
add_points(P, P, table[15 * i + x - 1]);
}
}
return P;
}
}
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);
}
const TwEdwardsCurve& Ed25519() {
static const TwEdwardsCurve Ed25519 = [] {
TwEdwardsCurve res(Fp25519()->frac(-121665, 121666), Fp25519()->frac(4, 5), Fp25519());
res.set_order_cofactor(hex_string{"80000000000000000000000000000000a6f7cef517bce6b2c09318d2e7ae9f68"}, 8);
res.build_table();
return res;
}();
return Ed25519;
}
} // namespace ellcurve

145
crypto/ellcurve/TwEdwards.h Normal file
View file

@ -0,0 +1,145 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include <vector>
#include "common/refcnt.hpp"
#include "openssl/residue.h"
#include "ellcurve/Fp25519.h"
namespace ellcurve {
using namespace arith;
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(td::Ref<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:
td::Ref<ResidueRing> ring;
Residue D;
Residue D2;
Residue Gy;
Bignum P_;
Bignum L;
Bignum Order;
Bignum cofactor;
int cofactor_short;
SegrePoint G;
SegrePoint O;
int table_lines;
std::vector<SegrePoint> table;
void init();
public:
TwEdwardsCurve(const Residue& _D, const Residue& _Gy, td::Ref<ResidueRing> _R);
~TwEdwardsCurve();
const Residue& get_gen_y() const {
return Gy;
}
const Bignum& get_ell() const {
return L;
}
const Bignum& get_order() const {
return Order;
}
td::Ref<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, bool uniform = false) const;
SegrePoint power_gen(const Bignum& n, bool uniform = false) const;
int build_table();
SegrePoint import_point(const unsigned char point[32], bool& ok) const;
};
std::ostream& operator<<(std::ostream& os, const TwEdwardsCurve::SegrePoint& P);
const TwEdwardsCurve& Ed25519();
} // namespace ellcurve