/* 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 . 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(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(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(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(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