mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			280 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|     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
 |