Move DhHandshake to DhHandshake.{cpp,h}.
GitOrigin-RevId: b34d9ae18e2106d3843223bc8609f5a89b6568f6
This commit is contained in:
parent
8a80571903
commit
7d28b73f5e
@ -328,6 +328,7 @@ set(TL_DOTNET_SCHEME_SOURCE
|
|||||||
set(TDLIB_SOURCE
|
set(TDLIB_SOURCE
|
||||||
td/mtproto/AuthData.cpp
|
td/mtproto/AuthData.cpp
|
||||||
td/mtproto/crypto.cpp
|
td/mtproto/crypto.cpp
|
||||||
|
td/mtproto/DhHandshake.cpp
|
||||||
td/mtproto/Handshake.cpp
|
td/mtproto/Handshake.cpp
|
||||||
td/mtproto/HandshakeActor.cpp
|
td/mtproto/HandshakeActor.cpp
|
||||||
td/mtproto/HttpTransport.cpp
|
td/mtproto/HttpTransport.cpp
|
||||||
@ -441,6 +442,7 @@ set(TDLIB_SOURCE
|
|||||||
td/mtproto/AuthKey.h
|
td/mtproto/AuthKey.h
|
||||||
td/mtproto/crypto.h
|
td/mtproto/crypto.h
|
||||||
td/mtproto/CryptoStorer.h
|
td/mtproto/CryptoStorer.h
|
||||||
|
td/mtproto/DhHandshake.h
|
||||||
td/mtproto/Handshake.h
|
td/mtproto/Handshake.h
|
||||||
td/mtproto/HandshakeActor.h
|
td/mtproto/HandshakeActor.h
|
||||||
td/mtproto/HandshakeConnection.h
|
td/mtproto/HandshakeConnection.h
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
//
|
//
|
||||||
#include "td/utils/benchmark.h" // for bench, do_not_optimize_away, etc
|
#include "td/utils/benchmark.h" // for bench, do_not_optimize_away, etc
|
||||||
|
|
||||||
#include "td/mtproto/crypto.h"
|
#include "td/mtproto/DhHandshake.h"
|
||||||
|
|
||||||
#include "td/utils/base64.h"
|
#include "td/utils/base64.h"
|
||||||
#include "td/utils/logging.h"
|
#include "td/utils/logging.h"
|
||||||
|
231
td/mtproto/DhHandshake.cpp
Normal file
231
td/mtproto/DhHandshake.cpp
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
//
|
||||||
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#include "td/mtproto/DhHandshake.h"
|
||||||
|
|
||||||
|
#include "td/utils/as.h"
|
||||||
|
#include "td/utils/crypto.h"
|
||||||
|
#include "td/utils/logging.h"
|
||||||
|
#include "td/utils/misc.h"
|
||||||
|
#include "td/utils/Random.h"
|
||||||
|
#include "td/utils/Slice.h"
|
||||||
|
#include "td/utils/Status.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace td {
|
||||||
|
|
||||||
|
Status DhHandshake::check_config(Slice prime_str, const BigNum &prime, int32 g_int, BigNumContext &ctx,
|
||||||
|
DhCallback *callback) {
|
||||||
|
// check that 2^2047 <= p < 2^2048
|
||||||
|
if (prime.get_num_bits() != 2048) {
|
||||||
|
return Status::Error("p is not 2048-bit number");
|
||||||
|
}
|
||||||
|
|
||||||
|
// g generates a cyclic subgroup of prime order (p - 1) / 2, i.e. is a quadratic residue mod p.
|
||||||
|
// Since g is always equal to 2, 3, 4, 5, 6 or 7, this is easily done using quadratic reciprocity law,
|
||||||
|
// yielding a simple condition on
|
||||||
|
// * p mod 4g - namely, p mod 8 = 7 for g = 2; p mod 3 = 2 for g = 3;
|
||||||
|
// * no extra condition for g = 4;
|
||||||
|
// * p mod 5 = 1 or 4 for g = 5;
|
||||||
|
// * p mod 24 = 19 or 23 for g = 6;
|
||||||
|
// * p mod 7 = 3, 5 or 6 for g = 7.
|
||||||
|
|
||||||
|
bool mod_ok;
|
||||||
|
uint32 mod_r;
|
||||||
|
switch (g_int) {
|
||||||
|
case 2:
|
||||||
|
mod_ok = prime % 8 == 7u;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
mod_ok = prime % 3 == 2u;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
mod_ok = true;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
mod_ok = (mod_r = prime % 5) == 1u || mod_r == 4u;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
mod_ok = (mod_r = prime % 24) == 19u || mod_r == 23u;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
mod_ok = (mod_r = prime % 7) == 3u || mod_r == 5u || mod_r == 6u;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mod_ok = false;
|
||||||
|
}
|
||||||
|
if (!mod_ok) {
|
||||||
|
return Status::Error("Bad prime mod 4g");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether p is a safe prime (meaning that both p and (p - 1) / 2 are prime)
|
||||||
|
int is_good_prime = -1;
|
||||||
|
if (callback) {
|
||||||
|
is_good_prime = callback->is_good_prime(prime_str);
|
||||||
|
}
|
||||||
|
if (is_good_prime != -1) {
|
||||||
|
return is_good_prime ? Status::OK() : Status::Error("p or (p - 1) / 2 is not a prime number");
|
||||||
|
}
|
||||||
|
if (!prime.is_prime(ctx)) {
|
||||||
|
if (callback) {
|
||||||
|
callback->add_bad_prime(prime_str);
|
||||||
|
}
|
||||||
|
return Status::Error("p is not a prime number");
|
||||||
|
}
|
||||||
|
|
||||||
|
BigNum half_prime = prime;
|
||||||
|
half_prime -= 1;
|
||||||
|
half_prime /= 2;
|
||||||
|
if (!half_prime.is_prime(ctx)) {
|
||||||
|
if (callback) {
|
||||||
|
callback->add_bad_prime(prime_str);
|
||||||
|
}
|
||||||
|
return Status::Error("(p - 1) / 2 is not a prime number");
|
||||||
|
}
|
||||||
|
if (callback) {
|
||||||
|
callback->add_good_prime(prime_str);
|
||||||
|
}
|
||||||
|
return Status::OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
Status DhHandshake::dh_check(const BigNum &prime, const BigNum &g_a, const BigNum &g_b) {
|
||||||
|
// IMPORTANT: Apart from the conditions on the Diffie-Hellman prime dh_prime and generator g, both sides are
|
||||||
|
// to check that g, g_a and g_b are greater than 1 and less than dh_prime - 1.
|
||||||
|
// We recommend checking that g_a and g_b are between 2^{2048-64} and dh_prime - 2^{2048-64} as well.
|
||||||
|
|
||||||
|
CHECK(prime.get_num_bits() == 2048);
|
||||||
|
BigNum left;
|
||||||
|
left.set_value(0);
|
||||||
|
left.set_bit(2048 - 64);
|
||||||
|
|
||||||
|
BigNum right;
|
||||||
|
BigNum::sub(right, prime, left);
|
||||||
|
|
||||||
|
if (BigNum::compare(left, g_a) > 0 || BigNum::compare(g_a, right) > 0 || BigNum::compare(left, g_b) > 0 ||
|
||||||
|
BigNum::compare(g_b, right) > 0) {
|
||||||
|
std::string x(2048, '0');
|
||||||
|
std::string y(2048, '0');
|
||||||
|
for (int i = 0; i < 2048; i++) {
|
||||||
|
if (g_a.is_bit_set(i)) {
|
||||||
|
x[i] = '1';
|
||||||
|
}
|
||||||
|
if (g_b.is_bit_set(i)) {
|
||||||
|
y[i] = '1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG(ERROR) << x;
|
||||||
|
LOG(ERROR) << y;
|
||||||
|
return Status::Error("g^a or g^b is not between 2^{2048-64} and dh_prime - 2^{2048-64}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Status::OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DhHandshake::set_config(int32 g_int, Slice prime_str) {
|
||||||
|
has_config_ = true;
|
||||||
|
prime_ = BigNum::from_binary(prime_str);
|
||||||
|
prime_str_ = prime_str.str();
|
||||||
|
|
||||||
|
b_ = BigNum();
|
||||||
|
g_b_ = BigNum();
|
||||||
|
|
||||||
|
BigNum::random(b_, 2048, -1, 0);
|
||||||
|
|
||||||
|
// g^b
|
||||||
|
g_int_ = g_int;
|
||||||
|
g_.set_value(g_int_);
|
||||||
|
|
||||||
|
BigNum::mod_exp(g_b_, g_, b_, prime_, ctx_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Status DhHandshake::check_config(int32 g_int, Slice prime_str, DhCallback *callback) {
|
||||||
|
BigNumContext ctx;
|
||||||
|
auto prime = BigNum::from_binary(prime_str);
|
||||||
|
return check_config(prime_str, prime, g_int, ctx, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DhHandshake::set_g_a_hash(Slice g_a_hash) {
|
||||||
|
has_g_a_hash_ = true;
|
||||||
|
ok_g_a_hash_ = false;
|
||||||
|
CHECK(!has_g_a_);
|
||||||
|
g_a_hash_ = g_a_hash.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DhHandshake::set_g_a(Slice g_a_str) {
|
||||||
|
has_g_a_ = true;
|
||||||
|
if (has_g_a_hash_) {
|
||||||
|
string g_a_hash(32, ' ');
|
||||||
|
sha256(g_a_str, g_a_hash);
|
||||||
|
ok_g_a_hash_ = g_a_hash == g_a_hash_;
|
||||||
|
}
|
||||||
|
g_a_ = BigNum::from_binary(g_a_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
string DhHandshake::get_g_a() const {
|
||||||
|
CHECK(has_g_a_);
|
||||||
|
return g_a_.to_binary();
|
||||||
|
}
|
||||||
|
|
||||||
|
string DhHandshake::get_g_b() const {
|
||||||
|
CHECK(has_config_);
|
||||||
|
return g_b_.to_binary();
|
||||||
|
}
|
||||||
|
string DhHandshake::get_g_b_hash() const {
|
||||||
|
string g_b_hash(32, ' ');
|
||||||
|
sha256(get_g_b(), g_b_hash);
|
||||||
|
return g_b_hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status DhHandshake::run_checks(bool skip_config_check, DhCallback *callback) {
|
||||||
|
CHECK(has_g_a_ && has_config_);
|
||||||
|
|
||||||
|
if (has_g_a_hash_ && !ok_g_a_hash_) {
|
||||||
|
return Status::Error("g_a_hash mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skip_config_check) {
|
||||||
|
TRY_STATUS(check_config(prime_str_, prime_, g_int_, ctx_, callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
return dh_check(prime_, g_a_, g_b_);
|
||||||
|
}
|
||||||
|
|
||||||
|
BigNum DhHandshake::get_g() const {
|
||||||
|
CHECK(has_config_);
|
||||||
|
return g_;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigNum DhHandshake::get_p() const {
|
||||||
|
CHECK(has_config_);
|
||||||
|
return prime_;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigNum DhHandshake::get_b() const {
|
||||||
|
CHECK(has_config_);
|
||||||
|
return b_;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigNum DhHandshake::get_g_ab() {
|
||||||
|
CHECK(has_g_a_ && has_config_);
|
||||||
|
BigNum g_ab;
|
||||||
|
BigNum::mod_exp(g_ab, g_a_, b_, prime_, ctx_);
|
||||||
|
return g_ab;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int64, string> DhHandshake::gen_key() {
|
||||||
|
string key = get_g_ab().to_binary(2048 / 8);
|
||||||
|
auto key_id = calc_key_id(key);
|
||||||
|
return std::pair<int64, string>(key_id, std::move(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
int64 DhHandshake::calc_key_id(Slice auth_key) {
|
||||||
|
UInt<160> auth_key_sha1;
|
||||||
|
sha1(auth_key, auth_key_sha1.raw);
|
||||||
|
return as<int64>(auth_key_sha1.raw + 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace td
|
135
td/mtproto/DhHandshake.h
Normal file
135
td/mtproto/DhHandshake.h
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
//
|
||||||
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "td/utils/BigNum.h"
|
||||||
|
#include "td/utils/common.h"
|
||||||
|
#include "td/utils/Slice.h"
|
||||||
|
#include "td/utils/Status.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace td {
|
||||||
|
|
||||||
|
/*** DH ***/
|
||||||
|
class DhCallback {
|
||||||
|
public:
|
||||||
|
DhCallback() = default;
|
||||||
|
DhCallback(const DhCallback &) = delete;
|
||||||
|
DhCallback &operator=(const DhCallback &) = delete;
|
||||||
|
DhCallback(DhCallback &&) = delete;
|
||||||
|
DhCallback &operator=(DhCallback &&) = delete;
|
||||||
|
virtual ~DhCallback() = default;
|
||||||
|
|
||||||
|
virtual int is_good_prime(Slice prime_str) const = 0;
|
||||||
|
virtual void add_good_prime(Slice prime_str) const = 0;
|
||||||
|
virtual void add_bad_prime(Slice prime_str) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DhHandshake {
|
||||||
|
public:
|
||||||
|
void set_config(int32 g_int, Slice prime_str);
|
||||||
|
|
||||||
|
static Status check_config(int32 g_int, Slice prime_str, DhCallback *callback);
|
||||||
|
|
||||||
|
bool has_config() const {
|
||||||
|
return has_config_;
|
||||||
|
}
|
||||||
|
void set_g_a_hash(Slice g_a_hash);
|
||||||
|
void set_g_a(Slice g_a_str);
|
||||||
|
bool has_g_a() const {
|
||||||
|
return has_g_a_;
|
||||||
|
}
|
||||||
|
string get_g_a() const;
|
||||||
|
string get_g_b() const;
|
||||||
|
string get_g_b_hash() const;
|
||||||
|
Status run_checks(bool skip_config_check, DhCallback *callback) TD_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
BigNum get_g() const;
|
||||||
|
BigNum get_p() const;
|
||||||
|
BigNum get_b() const;
|
||||||
|
BigNum get_g_ab();
|
||||||
|
|
||||||
|
std::pair<int64, string> gen_key();
|
||||||
|
|
||||||
|
static int64 calc_key_id(Slice auth_key);
|
||||||
|
|
||||||
|
enum Flags { HasConfig = 1, HasGA = 2 };
|
||||||
|
|
||||||
|
template <class StorerT>
|
||||||
|
void store(StorerT &storer) const {
|
||||||
|
auto flags = 0;
|
||||||
|
if (has_config_) {
|
||||||
|
flags |= HasConfig;
|
||||||
|
}
|
||||||
|
if (has_g_a_) {
|
||||||
|
flags |= HasGA;
|
||||||
|
}
|
||||||
|
storer.store_int(flags);
|
||||||
|
|
||||||
|
if (has_config_) {
|
||||||
|
// prime_, prime_str_, b_, g_, g_int_, g_b_
|
||||||
|
storer.store_string(prime_str_);
|
||||||
|
storer.store_string(b_.to_binary());
|
||||||
|
storer.store_int(g_int_);
|
||||||
|
storer.store_string(g_b_.to_binary());
|
||||||
|
}
|
||||||
|
if (has_g_a_) {
|
||||||
|
storer.store_string(g_a_.to_binary());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <class ParserT>
|
||||||
|
void parse(ParserT &parser) {
|
||||||
|
auto flags = parser.fetch_int();
|
||||||
|
if (flags & HasConfig) {
|
||||||
|
has_config_ = true;
|
||||||
|
}
|
||||||
|
if (flags & HasGA) {
|
||||||
|
has_g_a_ = true;
|
||||||
|
}
|
||||||
|
if (has_config_) {
|
||||||
|
// prime_, prime_str_, b_, g_, g_int_, g_b_
|
||||||
|
prime_str_ = parser.template fetch_string<std::string>();
|
||||||
|
prime_ = BigNum::from_binary(prime_str_);
|
||||||
|
|
||||||
|
b_ = BigNum::from_binary(parser.template fetch_string<string>());
|
||||||
|
|
||||||
|
g_int_ = parser.fetch_int();
|
||||||
|
g_.set_value(g_int_);
|
||||||
|
|
||||||
|
g_b_ = BigNum::from_binary(parser.template fetch_string<string>());
|
||||||
|
}
|
||||||
|
if (has_g_a_) {
|
||||||
|
g_a_ = BigNum::from_binary(parser.template fetch_string<string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Status check_config(Slice prime_str, const BigNum &prime, int32 g_int, BigNumContext &ctx,
|
||||||
|
DhCallback *callback) TD_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
static Status dh_check(const BigNum &prime, const BigNum &g_a, const BigNum &g_b) TD_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
string prime_str_;
|
||||||
|
BigNum prime_;
|
||||||
|
BigNum g_;
|
||||||
|
int32 g_int_;
|
||||||
|
BigNum b_;
|
||||||
|
BigNum g_b_;
|
||||||
|
BigNum g_a_;
|
||||||
|
|
||||||
|
string g_a_hash_;
|
||||||
|
bool has_g_a_hash_{false};
|
||||||
|
bool ok_g_a_hash_{false};
|
||||||
|
|
||||||
|
bool has_config_ = false;
|
||||||
|
bool has_g_a_ = false;
|
||||||
|
|
||||||
|
BigNumContext ctx_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace td
|
@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
#include "td/mtproto/Handshake.h"
|
#include "td/mtproto/Handshake.h"
|
||||||
|
|
||||||
|
#include "td/mtproto/crypto.h"
|
||||||
#include "td/mtproto/utils.h"
|
#include "td/mtproto/utils.h"
|
||||||
|
|
||||||
#include "td/mtproto/mtproto_api.h"
|
#include "td/mtproto/mtproto_api.h"
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "td/mtproto/AuthKey.h"
|
#include "td/mtproto/AuthKey.h"
|
||||||
#include "td/mtproto/crypto.h"
|
#include "td/mtproto/crypto.h"
|
||||||
|
#include "td/mtproto/DhHandshake.h"
|
||||||
|
|
||||||
#include "td/utils/buffer.h"
|
#include "td/utils/buffer.h"
|
||||||
#include "td/utils/Slice.h"
|
#include "td/utils/Slice.h"
|
||||||
|
@ -12,8 +12,6 @@
|
|||||||
#include "td/mtproto/PacketInfo.h"
|
#include "td/mtproto/PacketInfo.h"
|
||||||
#include "td/mtproto/PacketStorer.h"
|
#include "td/mtproto/PacketStorer.h"
|
||||||
#include "td/mtproto/RawConnection.h"
|
#include "td/mtproto/RawConnection.h"
|
||||||
#include "td/mtproto/Transport.h"
|
|
||||||
#include "td/mtproto/utils.h"
|
|
||||||
|
|
||||||
#include "td/utils/buffer.h"
|
#include "td/utils/buffer.h"
|
||||||
#include "td/utils/common.h"
|
#include "td/utils/common.h"
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "td/mtproto/CryptoStorer.h"
|
#include "td/mtproto/CryptoStorer.h"
|
||||||
#include "td/mtproto/PacketStorer.h"
|
#include "td/mtproto/PacketStorer.h"
|
||||||
#include "td/mtproto/Transport.h"
|
#include "td/mtproto/Transport.h"
|
||||||
|
#include "td/mtproto/utils.h"
|
||||||
|
|
||||||
#include "td/utils/as.h"
|
#include "td/utils/as.h"
|
||||||
#include "td/utils/format.h"
|
#include "td/utils/format.h"
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "td/mtproto/RawConnection.h"
|
|
||||||
#include "td/mtproto/PacketInfo.h"
|
#include "td/mtproto/PacketInfo.h"
|
||||||
#include "td/mtproto/Query.h"
|
#include "td/mtproto/Query.h"
|
||||||
|
#include "td/mtproto/RawConnection.h"
|
||||||
|
|
||||||
#include "td/utils/buffer.h"
|
#include "td/utils/buffer.h"
|
||||||
#include "td/utils/format.h"
|
#include "td/utils/format.h"
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "td/mtproto/IStreamTransport.h"
|
#include "td/mtproto/IStreamTransport.h"
|
||||||
|
#include "td/mtproto/TransportType.h"
|
||||||
|
|
||||||
#include "td/utils/AesCtrByteFlow.h"
|
#include "td/utils/AesCtrByteFlow.h"
|
||||||
#include "td/utils/buffer.h"
|
#include "td/utils/buffer.h"
|
||||||
|
@ -130,216 +130,6 @@ void RSA::decrypt(Slice from, MutableSlice to) const {
|
|||||||
std::memcpy(to.data(), result.c_str(), 256);
|
std::memcpy(to.data(), result.c_str(), 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
Status DhHandshake::check_config(Slice prime_str, const BigNum &prime, int32 g_int, BigNumContext &ctx,
|
|
||||||
DhCallback *callback) {
|
|
||||||
// check that 2^2047 <= p < 2^2048
|
|
||||||
if (prime.get_num_bits() != 2048) {
|
|
||||||
return Status::Error("p is not 2048-bit number");
|
|
||||||
}
|
|
||||||
|
|
||||||
// g generates a cyclic subgroup of prime order (p - 1) / 2, i.e. is a quadratic residue mod p.
|
|
||||||
// Since g is always equal to 2, 3, 4, 5, 6 or 7, this is easily done using quadratic reciprocity law,
|
|
||||||
// yielding a simple condition on
|
|
||||||
// * p mod 4g - namely, p mod 8 = 7 for g = 2; p mod 3 = 2 for g = 3;
|
|
||||||
// * no extra condition for g = 4;
|
|
||||||
// * p mod 5 = 1 or 4 for g = 5;
|
|
||||||
// * p mod 24 = 19 or 23 for g = 6;
|
|
||||||
// * p mod 7 = 3, 5 or 6 for g = 7.
|
|
||||||
|
|
||||||
bool mod_ok;
|
|
||||||
uint32 mod_r;
|
|
||||||
switch (g_int) {
|
|
||||||
case 2:
|
|
||||||
mod_ok = prime % 8 == 7u;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
mod_ok = prime % 3 == 2u;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
mod_ok = true;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
mod_ok = (mod_r = prime % 5) == 1u || mod_r == 4u;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
mod_ok = (mod_r = prime % 24) == 19u || mod_r == 23u;
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
mod_ok = (mod_r = prime % 7) == 3u || mod_r == 5u || mod_r == 6u;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
mod_ok = false;
|
|
||||||
}
|
|
||||||
if (!mod_ok) {
|
|
||||||
return Status::Error("Bad prime mod 4g");
|
|
||||||
}
|
|
||||||
|
|
||||||
// check whether p is a safe prime (meaning that both p and (p - 1) / 2 are prime)
|
|
||||||
int is_good_prime = -1;
|
|
||||||
if (callback) {
|
|
||||||
is_good_prime = callback->is_good_prime(prime_str);
|
|
||||||
}
|
|
||||||
if (is_good_prime != -1) {
|
|
||||||
return is_good_prime ? Status::OK() : Status::Error("p or (p - 1) / 2 is not a prime number");
|
|
||||||
}
|
|
||||||
if (!prime.is_prime(ctx)) {
|
|
||||||
if (callback) {
|
|
||||||
callback->add_bad_prime(prime_str);
|
|
||||||
}
|
|
||||||
return Status::Error("p is not a prime number");
|
|
||||||
}
|
|
||||||
|
|
||||||
BigNum half_prime = prime;
|
|
||||||
half_prime -= 1;
|
|
||||||
half_prime /= 2;
|
|
||||||
if (!half_prime.is_prime(ctx)) {
|
|
||||||
if (callback) {
|
|
||||||
callback->add_bad_prime(prime_str);
|
|
||||||
}
|
|
||||||
return Status::Error("(p - 1) / 2 is not a prime number");
|
|
||||||
}
|
|
||||||
if (callback) {
|
|
||||||
callback->add_good_prime(prime_str);
|
|
||||||
}
|
|
||||||
return Status::OK();
|
|
||||||
}
|
|
||||||
|
|
||||||
Status DhHandshake::dh_check(const BigNum &prime, const BigNum &g_a, const BigNum &g_b) {
|
|
||||||
// IMPORTANT: Apart from the conditions on the Diffie-Hellman prime dh_prime and generator g, both sides are
|
|
||||||
// to check that g, g_a and g_b are greater than 1 and less than dh_prime - 1.
|
|
||||||
// We recommend checking that g_a and g_b are between 2^{2048-64} and dh_prime - 2^{2048-64} as well.
|
|
||||||
|
|
||||||
CHECK(prime.get_num_bits() == 2048);
|
|
||||||
BigNum left;
|
|
||||||
left.set_value(0);
|
|
||||||
left.set_bit(2048 - 64);
|
|
||||||
|
|
||||||
BigNum right;
|
|
||||||
BigNum::sub(right, prime, left);
|
|
||||||
|
|
||||||
if (BigNum::compare(left, g_a) > 0 || BigNum::compare(g_a, right) > 0 || BigNum::compare(left, g_b) > 0 ||
|
|
||||||
BigNum::compare(g_b, right) > 0) {
|
|
||||||
std::string x(2048, '0');
|
|
||||||
std::string y(2048, '0');
|
|
||||||
for (int i = 0; i < 2048; i++) {
|
|
||||||
if (g_a.is_bit_set(i)) {
|
|
||||||
x[i] = '1';
|
|
||||||
}
|
|
||||||
if (g_b.is_bit_set(i)) {
|
|
||||||
y[i] = '1';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG(ERROR) << x;
|
|
||||||
LOG(ERROR) << y;
|
|
||||||
return Status::Error("g^a or g^b is not between 2^{2048-64} and dh_prime - 2^{2048-64}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Status::OK();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DhHandshake::set_config(int32 g_int, Slice prime_str) {
|
|
||||||
has_config_ = true;
|
|
||||||
prime_ = BigNum::from_binary(prime_str);
|
|
||||||
prime_str_ = prime_str.str();
|
|
||||||
|
|
||||||
b_ = BigNum();
|
|
||||||
g_b_ = BigNum();
|
|
||||||
|
|
||||||
BigNum::random(b_, 2048, -1, 0);
|
|
||||||
|
|
||||||
// g^b
|
|
||||||
g_int_ = g_int;
|
|
||||||
g_.set_value(g_int_);
|
|
||||||
|
|
||||||
BigNum::mod_exp(g_b_, g_, b_, prime_, ctx_);
|
|
||||||
}
|
|
||||||
|
|
||||||
Status DhHandshake::check_config(int32 g_int, Slice prime_str, DhCallback *callback) {
|
|
||||||
BigNumContext ctx;
|
|
||||||
auto prime = BigNum::from_binary(prime_str);
|
|
||||||
return check_config(prime_str, prime, g_int, ctx, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DhHandshake::set_g_a_hash(Slice g_a_hash) {
|
|
||||||
has_g_a_hash_ = true;
|
|
||||||
ok_g_a_hash_ = false;
|
|
||||||
CHECK(!has_g_a_);
|
|
||||||
g_a_hash_ = g_a_hash.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DhHandshake::set_g_a(Slice g_a_str) {
|
|
||||||
has_g_a_ = true;
|
|
||||||
if (has_g_a_hash_) {
|
|
||||||
string g_a_hash(32, ' ');
|
|
||||||
sha256(g_a_str, g_a_hash);
|
|
||||||
ok_g_a_hash_ = g_a_hash == g_a_hash_;
|
|
||||||
}
|
|
||||||
g_a_ = BigNum::from_binary(g_a_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
string DhHandshake::get_g_a() const {
|
|
||||||
CHECK(has_g_a_);
|
|
||||||
return g_a_.to_binary();
|
|
||||||
}
|
|
||||||
|
|
||||||
string DhHandshake::get_g_b() const {
|
|
||||||
CHECK(has_config_);
|
|
||||||
return g_b_.to_binary();
|
|
||||||
}
|
|
||||||
string DhHandshake::get_g_b_hash() const {
|
|
||||||
string g_b_hash(32, ' ');
|
|
||||||
sha256(get_g_b(), g_b_hash);
|
|
||||||
return g_b_hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status DhHandshake::run_checks(bool skip_config_check, DhCallback *callback) {
|
|
||||||
CHECK(has_g_a_ && has_config_);
|
|
||||||
|
|
||||||
if (has_g_a_hash_ && !ok_g_a_hash_) {
|
|
||||||
return Status::Error("g_a_hash mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!skip_config_check) {
|
|
||||||
TRY_STATUS(check_config(prime_str_, prime_, g_int_, ctx_, callback));
|
|
||||||
}
|
|
||||||
|
|
||||||
return dh_check(prime_, g_a_, g_b_);
|
|
||||||
}
|
|
||||||
|
|
||||||
BigNum DhHandshake::get_g() const {
|
|
||||||
CHECK(has_config_);
|
|
||||||
return g_;
|
|
||||||
}
|
|
||||||
|
|
||||||
BigNum DhHandshake::get_p() const {
|
|
||||||
CHECK(has_config_);
|
|
||||||
return prime_;
|
|
||||||
}
|
|
||||||
|
|
||||||
BigNum DhHandshake::get_b() const {
|
|
||||||
CHECK(has_config_);
|
|
||||||
return b_;
|
|
||||||
}
|
|
||||||
|
|
||||||
BigNum DhHandshake::get_g_ab() {
|
|
||||||
CHECK(has_g_a_ && has_config_);
|
|
||||||
BigNum g_ab;
|
|
||||||
BigNum::mod_exp(g_ab, g_a_, b_, prime_, ctx_);
|
|
||||||
return g_ab;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<int64, string> DhHandshake::gen_key() {
|
|
||||||
string key = get_g_ab().to_binary(2048 / 8);
|
|
||||||
auto key_id = calc_key_id(key);
|
|
||||||
return std::pair<int64, string>(key_id, std::move(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
int64 DhHandshake::calc_key_id(Slice auth_key) {
|
|
||||||
UInt<160> auth_key_sha1;
|
|
||||||
sha1(auth_key, auth_key_sha1.raw);
|
|
||||||
return as<int64>(auth_key_sha1.raw + 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** KDF ***/
|
/*** KDF ***/
|
||||||
void KDF(const string &auth_key, const UInt128 &msg_key, int X, UInt256 *aes_key, UInt256 *aes_iv) {
|
void KDF(const string &auth_key, const UInt128 &msg_key, int X, UInt256 *aes_key, UInt256 *aes_iv) {
|
||||||
CHECK(auth_key.size() == 2048 / 8);
|
CHECK(auth_key.size() == 2048 / 8);
|
||||||
@ -434,4 +224,5 @@ void KDF2(Slice auth_key, const UInt128 &msg_key, int X, UInt256 *aes_key, UInt2
|
|||||||
aes_iv_slice.substr(8).copy_from(sha256_a.substr(8, 16));
|
aes_iv_slice.substr(8).copy_from(sha256_a.substr(8, 16));
|
||||||
aes_iv_slice.substr(24).copy_from(sha256_b.substr(24, 8));
|
aes_iv_slice.substr(24).copy_from(sha256_b.substr(24, 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace td
|
} // namespace td
|
||||||
|
@ -42,123 +42,6 @@ class PublicRsaKeyInterface {
|
|||||||
virtual void drop_keys() = 0;
|
virtual void drop_keys() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*** DH ***/
|
|
||||||
class DhCallback {
|
|
||||||
public:
|
|
||||||
DhCallback() = default;
|
|
||||||
DhCallback(const DhCallback &) = delete;
|
|
||||||
DhCallback &operator=(const DhCallback &) = delete;
|
|
||||||
DhCallback(DhCallback &&) = delete;
|
|
||||||
DhCallback &operator=(DhCallback &&) = delete;
|
|
||||||
virtual ~DhCallback() = default;
|
|
||||||
|
|
||||||
virtual int is_good_prime(Slice prime_str) const = 0;
|
|
||||||
virtual void add_good_prime(Slice prime_str) const = 0;
|
|
||||||
virtual void add_bad_prime(Slice prime_str) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DhHandshake {
|
|
||||||
public:
|
|
||||||
void set_config(int32 g_int, Slice prime_str);
|
|
||||||
|
|
||||||
static Status check_config(int32 g_int, Slice prime_str, DhCallback *callback);
|
|
||||||
|
|
||||||
bool has_config() const {
|
|
||||||
return has_config_;
|
|
||||||
}
|
|
||||||
void set_g_a_hash(Slice g_a_hash);
|
|
||||||
void set_g_a(Slice g_a_str);
|
|
||||||
bool has_g_a() const {
|
|
||||||
return has_g_a_;
|
|
||||||
}
|
|
||||||
string get_g_a() const;
|
|
||||||
string get_g_b() const;
|
|
||||||
string get_g_b_hash() const;
|
|
||||||
Status run_checks(bool skip_config_check, DhCallback *callback) TD_WARN_UNUSED_RESULT;
|
|
||||||
|
|
||||||
BigNum get_g() const;
|
|
||||||
BigNum get_p() const;
|
|
||||||
BigNum get_b() const;
|
|
||||||
BigNum get_g_ab();
|
|
||||||
|
|
||||||
std::pair<int64, string> gen_key();
|
|
||||||
|
|
||||||
static int64 calc_key_id(Slice auth_key);
|
|
||||||
|
|
||||||
enum Flags { HasConfig = 1, HasGA = 2 };
|
|
||||||
|
|
||||||
template <class StorerT>
|
|
||||||
void store(StorerT &storer) const {
|
|
||||||
auto flags = 0;
|
|
||||||
if (has_config_) {
|
|
||||||
flags |= HasConfig;
|
|
||||||
}
|
|
||||||
if (has_g_a_) {
|
|
||||||
flags |= HasGA;
|
|
||||||
}
|
|
||||||
storer.store_int(flags);
|
|
||||||
|
|
||||||
if (has_config_) {
|
|
||||||
// prime_, prime_str_, b_, g_, g_int_, g_b_
|
|
||||||
storer.store_string(prime_str_);
|
|
||||||
storer.store_string(b_.to_binary());
|
|
||||||
storer.store_int(g_int_);
|
|
||||||
storer.store_string(g_b_.to_binary());
|
|
||||||
}
|
|
||||||
if (has_g_a_) {
|
|
||||||
storer.store_string(g_a_.to_binary());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <class ParserT>
|
|
||||||
void parse(ParserT &parser) {
|
|
||||||
auto flags = parser.fetch_int();
|
|
||||||
if (flags & HasConfig) {
|
|
||||||
has_config_ = true;
|
|
||||||
}
|
|
||||||
if (flags & HasGA) {
|
|
||||||
has_g_a_ = true;
|
|
||||||
}
|
|
||||||
if (has_config_) {
|
|
||||||
// prime_, prime_str_, b_, g_, g_int_, g_b_
|
|
||||||
prime_str_ = parser.template fetch_string<std::string>();
|
|
||||||
prime_ = BigNum::from_binary(prime_str_);
|
|
||||||
|
|
||||||
b_ = BigNum::from_binary(parser.template fetch_string<string>());
|
|
||||||
|
|
||||||
g_int_ = parser.fetch_int();
|
|
||||||
g_.set_value(g_int_);
|
|
||||||
|
|
||||||
g_b_ = BigNum::from_binary(parser.template fetch_string<string>());
|
|
||||||
}
|
|
||||||
if (has_g_a_) {
|
|
||||||
g_a_ = BigNum::from_binary(parser.template fetch_string<string>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static Status check_config(Slice prime_str, const BigNum &prime, int32 g_int, BigNumContext &ctx,
|
|
||||||
DhCallback *callback) TD_WARN_UNUSED_RESULT;
|
|
||||||
|
|
||||||
static Status dh_check(const BigNum &prime, const BigNum &g_a, const BigNum &g_b) TD_WARN_UNUSED_RESULT;
|
|
||||||
|
|
||||||
string prime_str_;
|
|
||||||
BigNum prime_;
|
|
||||||
BigNum g_;
|
|
||||||
int32 g_int_;
|
|
||||||
BigNum b_;
|
|
||||||
BigNum g_b_;
|
|
||||||
BigNum g_a_;
|
|
||||||
|
|
||||||
string g_a_hash_;
|
|
||||||
bool has_g_a_hash_{false};
|
|
||||||
bool ok_g_a_hash_{false};
|
|
||||||
|
|
||||||
bool has_config_ = false;
|
|
||||||
bool has_g_a_ = false;
|
|
||||||
|
|
||||||
BigNumContext ctx_;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*** KDF ***/
|
/*** KDF ***/
|
||||||
void KDF(const string &auth_key, const UInt128 &msg_key, int X, UInt256 *aes_key, UInt256 *aes_iv);
|
void KDF(const string &auth_key, const UInt128 &msg_key, int X, UInt256 *aes_key, UInt256 *aes_iv);
|
||||||
void tmp_KDF(const UInt128 &server_nonce, const UInt256 &new_nonce, UInt256 *tmp_aes_key, UInt256 *tmp_aes_iv);
|
void tmp_KDF(const UInt128 &server_nonce, const UInt256 &new_nonce, UInt256 *tmp_aes_key, UInt256 *tmp_aes_iv);
|
||||||
|
@ -10,8 +10,6 @@
|
|||||||
#include "td/telegram/telegram_api.h"
|
#include "td/telegram/telegram_api.h"
|
||||||
#include "td/telegram/telegram_api.hpp"
|
#include "td/telegram/telegram_api.hpp"
|
||||||
|
|
||||||
#include "td/mtproto/crypto.h"
|
|
||||||
|
|
||||||
#include "td/telegram/ConfigShared.h"
|
#include "td/telegram/ConfigShared.h"
|
||||||
#include "td/telegram/ContactsManager.h"
|
#include "td/telegram/ContactsManager.h"
|
||||||
#include "td/telegram/DhCache.h"
|
#include "td/telegram/DhCache.h"
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#include "td/telegram/td_api.h"
|
#include "td/telegram/td_api.h"
|
||||||
#include "td/telegram/telegram_api.h"
|
#include "td/telegram/telegram_api.h"
|
||||||
|
|
||||||
#include "td/mtproto/crypto.h"
|
#include "td/mtproto/DhHandshake.h"
|
||||||
|
|
||||||
#include "td/telegram/CallDiscardReason.h"
|
#include "td/telegram/CallDiscardReason.h"
|
||||||
#include "td/telegram/CallId.h"
|
#include "td/telegram/CallId.h"
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
#include "td/telegram/td_api.hpp"
|
#include "td/telegram/td_api.hpp"
|
||||||
#include "td/telegram/telegram_api.h"
|
#include "td/telegram/telegram_api.h"
|
||||||
|
|
||||||
#include "td/mtproto/crypto.h"
|
#include "td/mtproto/DhHandshake.h"
|
||||||
|
|
||||||
#include "td/utils/base64.h"
|
#include "td/utils/base64.h"
|
||||||
#include "td/utils/buffer.h"
|
#include "td/utils/buffer.h"
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "td/mtproto/crypto.h"
|
#include "td/mtproto/DhHandshake.h"
|
||||||
|
|
||||||
#include "td/utils/Slice.h"
|
#include "td/utils/Slice.h"
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
#include "td/telegram/SecureStorage.h"
|
#include "td/telegram/SecureStorage.h"
|
||||||
#include "td/telegram/TdDb.h"
|
#include "td/telegram/TdDb.h"
|
||||||
|
|
||||||
#include "td/mtproto/crypto.h"
|
#include "td/mtproto/DhHandshake.h"
|
||||||
|
|
||||||
#include "td/utils/BigNum.h"
|
#include "td/utils/BigNum.h"
|
||||||
#include "td/utils/buffer.h"
|
#include "td/utils/buffer.h"
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
#include "td/actor/PromiseFuture.h"
|
#include "td/actor/PromiseFuture.h"
|
||||||
|
|
||||||
#include "td/mtproto/AuthKey.h"
|
#include "td/mtproto/AuthKey.h"
|
||||||
#include "td/mtproto/crypto.h"
|
#include "td/mtproto/DhHandshake.h"
|
||||||
|
|
||||||
#include "td/telegram/DhConfig.h"
|
#include "td/telegram/DhConfig.h"
|
||||||
#include "td/telegram/logevent/SecretChatEvent.h"
|
#include "td/telegram/logevent/SecretChatEvent.h"
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#include "td/telegram/secret_api.h"
|
#include "td/telegram/secret_api.h"
|
||||||
#include "td/telegram/telegram_api.hpp"
|
#include "td/telegram/telegram_api.hpp"
|
||||||
|
|
||||||
#include "td/mtproto/crypto.h"
|
#include "td/mtproto/DhHandshake.h"
|
||||||
|
|
||||||
#include "td/actor/PromiseFuture.h"
|
#include "td/actor/PromiseFuture.h"
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "td/telegram/UniqueId.h"
|
#include "td/telegram/UniqueId.h"
|
||||||
|
|
||||||
#include "td/mtproto/crypto.h"
|
#include "td/mtproto/crypto.h"
|
||||||
|
#include "td/mtproto/DhHandshake.h"
|
||||||
#include "td/mtproto/Handshake.h"
|
#include "td/mtproto/Handshake.h"
|
||||||
#include "td/mtproto/HandshakeActor.h"
|
#include "td/mtproto/HandshakeActor.h"
|
||||||
#include "td/mtproto/RawConnection.h"
|
#include "td/mtproto/RawConnection.h"
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
#include "td/actor/PromiseFuture.h"
|
#include "td/actor/PromiseFuture.h"
|
||||||
|
|
||||||
#include "td/mtproto/crypto.h"
|
#include "td/mtproto/crypto.h"
|
||||||
|
#include "td/mtproto/DhHandshake.h"
|
||||||
#include "td/mtproto/Handshake.h"
|
#include "td/mtproto/Handshake.h"
|
||||||
#include "td/mtproto/HandshakeActor.h"
|
#include "td/mtproto/HandshakeActor.h"
|
||||||
#include "td/mtproto/PingConnection.h"
|
#include "td/mtproto/PingConnection.h"
|
||||||
#include "td/mtproto/RawConnection.h"
|
#include "td/mtproto/RawConnection.h"
|
||||||
#include "td/mtproto/Transport.h"
|
|
||||||
#include "td/mtproto/TransportType.h"
|
#include "td/mtproto/TransportType.h"
|
||||||
|
|
||||||
#include "td/net/GetHostByNameActor.h"
|
#include "td/net/GetHostByNameActor.h"
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#include "td/db/BinlogKeyValue.h"
|
#include "td/db/BinlogKeyValue.h"
|
||||||
#include "td/db/DbKey.h"
|
#include "td/db/DbKey.h"
|
||||||
|
|
||||||
#include "td/mtproto/crypto.h"
|
#include "td/mtproto/DhHandshake.h"
|
||||||
#include "td/mtproto/utils.h"
|
#include "td/mtproto/utils.h"
|
||||||
|
|
||||||
#include "td/tl/tl_object_parse.h"
|
#include "td/tl/tl_object_parse.h"
|
||||||
|
Reference in New Issue
Block a user