1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/mtproto/DhHandshake.h"
8 
9 #include "td/mtproto/DhCallback.h"
10 
11 #include "td/utils/as.h"
12 #include "td/utils/crypto.h"
13 #include "td/utils/logging.h"
14 #include "td/utils/Slice.h"
15 #include "td/utils/Status.h"
16 #include "td/utils/UInt.h"
17 
18 namespace td {
19 namespace mtproto {
20 
check_config(Slice prime_str,const BigNum & prime,int32 g_int,BigNumContext & ctx,DhCallback * callback)21 Status DhHandshake::check_config(Slice prime_str, const BigNum &prime, int32 g_int, BigNumContext &ctx,
22                                  DhCallback *callback) {
23   // check that 2^2047 <= p < 2^2048
24   if (prime.get_num_bits() != 2048) {
25     return Status::Error("p is not 2048-bit number");
26   }
27 
28   // g generates a cyclic subgroup of prime order (p - 1) / 2, i.e. is a quadratic residue mod p.
29   // Since g is always equal to 2, 3, 4, 5, 6 or 7, this is easily done using quadratic reciprocity law,
30   // yielding a simple condition on
31   // * p mod 4g - namely, p mod 8 = 7 for g = 2; p mod 3 = 2 for g = 3;
32   // * no extra condition for g = 4;
33   // * p mod 5 = 1 or 4 for g = 5;
34   // * p mod 24 = 19 or 23 for g = 6;
35   // * p mod 7 = 3, 5 or 6 for g = 7.
36 
37   bool mod_ok;
38   uint32 mod_r;
39   switch (g_int) {
40     case 2:
41       mod_ok = prime % 8 == 7u;
42       break;
43     case 3:
44       mod_ok = prime % 3 == 2u;
45       break;
46     case 4:
47       mod_ok = true;
48       break;
49     case 5:
50       mod_ok = (mod_r = prime % 5) == 1u || mod_r == 4u;
51       break;
52     case 6:
53       mod_ok = (mod_r = prime % 24) == 19u || mod_r == 23u;
54       break;
55     case 7:
56       mod_ok = (mod_r = prime % 7) == 3u || mod_r == 5u || mod_r == 6u;
57       break;
58     default:
59       mod_ok = false;
60   }
61   if (!mod_ok) {
62     return Status::Error("Bad prime mod 4g");
63   }
64 
65   // check whether p is a safe prime (meaning that both p and (p - 1) / 2 are prime)
66   int is_good_prime = -1;
67   if (callback) {
68     is_good_prime = callback->is_good_prime(prime_str);
69   }
70   if (is_good_prime != -1) {
71     return is_good_prime ? Status::OK() : Status::Error("p or (p - 1) / 2 is not a prime number");
72   }
73   if (!prime.is_prime(ctx)) {
74     if (callback) {
75       callback->add_bad_prime(prime_str);
76     }
77     return Status::Error("p is not a prime number");
78   }
79 
80   BigNum half_prime = prime;
81   half_prime -= 1;
82   half_prime /= 2;
83   if (!half_prime.is_prime(ctx)) {
84     if (callback) {
85       callback->add_bad_prime(prime_str);
86     }
87     return Status::Error("(p - 1) / 2 is not a prime number");
88   }
89   if (callback) {
90     callback->add_good_prime(prime_str);
91   }
92   return Status::OK();
93 }
94 
dh_check(const BigNum & prime,const BigNum & g_a,const BigNum & g_b)95 Status DhHandshake::dh_check(const BigNum &prime, const BigNum &g_a, const BigNum &g_b) {
96   // IMPORTANT: Apart from the conditions on the Diffie-Hellman prime dh_prime and generator g, both sides are
97   // to check that g, g_a and g_b are greater than 1 and less than dh_prime - 1.
98   // We recommend checking that g_a and g_b are between 2^{2048-64} and dh_prime - 2^{2048-64} as well.
99 
100   CHECK(prime.get_num_bits() == 2048);
101   BigNum left;
102   left.set_value(0);
103   left.set_bit(2048 - 64);
104 
105   BigNum right;
106   BigNum::sub(right, prime, left);
107 
108   if (BigNum::compare(left, g_a) > 0 || BigNum::compare(g_a, right) > 0 || BigNum::compare(left, g_b) > 0 ||
109       BigNum::compare(g_b, right) > 0) {
110     std::string x(2048, '0');
111     std::string y(2048, '0');
112     for (int i = 0; i < 2048; i++) {
113       if (g_a.is_bit_set(i)) {
114         x[i] = '1';
115       }
116       if (g_b.is_bit_set(i)) {
117         y[i] = '1';
118       }
119     }
120     LOG(ERROR) << x;
121     LOG(ERROR) << y;
122     return Status::Error("g^a or g^b is not between 2^{2048-64} and dh_prime - 2^{2048-64}");
123   }
124 
125   return Status::OK();
126 }
127 
set_config(int32 g_int,Slice prime_str)128 void DhHandshake::set_config(int32 g_int, Slice prime_str) {
129   has_config_ = true;
130   prime_ = BigNum::from_binary(prime_str);
131   prime_str_ = prime_str.str();
132 
133   b_ = BigNum();
134   g_b_ = BigNum();
135 
136   BigNum::random(b_, 2048, -1, 0);
137 
138   // g^b
139   g_int_ = g_int;
140   g_.set_value(g_int_);
141 
142   BigNum::mod_exp(g_b_, g_, b_, prime_, ctx_);
143 }
144 
check_config(int32 g_int,Slice prime_str,DhCallback * callback)145 Status DhHandshake::check_config(int32 g_int, Slice prime_str, DhCallback *callback) {
146   BigNumContext ctx;
147   auto prime = BigNum::from_binary(prime_str);
148   return check_config(prime_str, prime, g_int, ctx, callback);
149 }
150 
set_g_a_hash(Slice g_a_hash)151 void DhHandshake::set_g_a_hash(Slice g_a_hash) {
152   has_g_a_hash_ = true;
153   ok_g_a_hash_ = false;
154   CHECK(!has_g_a_);
155   g_a_hash_ = g_a_hash.str();
156 }
157 
set_g_a(Slice g_a_str)158 void DhHandshake::set_g_a(Slice g_a_str) {
159   has_g_a_ = true;
160   if (has_g_a_hash_) {
161     string g_a_hash(32, ' ');
162     sha256(g_a_str, g_a_hash);
163     ok_g_a_hash_ = g_a_hash == g_a_hash_;
164   }
165   g_a_ = BigNum::from_binary(g_a_str);
166 }
167 
get_g_a() const168 string DhHandshake::get_g_a() const {
169   CHECK(has_g_a_);
170   return g_a_.to_binary();
171 }
172 
get_g_b() const173 string DhHandshake::get_g_b() const {
174   CHECK(has_config_);
175   return g_b_.to_binary();
176 }
get_g_b_hash() const177 string DhHandshake::get_g_b_hash() const {
178   string g_b_hash(32, ' ');
179   sha256(get_g_b(), g_b_hash);
180   return g_b_hash;
181 }
182 
run_checks(bool skip_config_check,DhCallback * callback)183 Status DhHandshake::run_checks(bool skip_config_check, DhCallback *callback) {
184   CHECK(has_g_a_ && has_config_);
185 
186   if (has_g_a_hash_ && !ok_g_a_hash_) {
187     return Status::Error("g_a_hash mismatch");
188   }
189 
190   if (!skip_config_check) {
191     TRY_STATUS(check_config(prime_str_, prime_, g_int_, ctx_, callback));
192   }
193 
194   return dh_check(prime_, g_a_, g_b_);
195 }
196 
get_g() const197 BigNum DhHandshake::get_g() const {
198   CHECK(has_config_);
199   return g_;
200 }
201 
get_p() const202 BigNum DhHandshake::get_p() const {
203   CHECK(has_config_);
204   return prime_;
205 }
206 
get_b() const207 BigNum DhHandshake::get_b() const {
208   CHECK(has_config_);
209   return b_;
210 }
211 
get_g_ab()212 BigNum DhHandshake::get_g_ab() {
213   CHECK(has_g_a_ && has_config_);
214   BigNum g_ab;
215   BigNum::mod_exp(g_ab, g_a_, b_, prime_, ctx_);
216   return g_ab;
217 }
218 
gen_key()219 std::pair<int64, string> DhHandshake::gen_key() {
220   string key = get_g_ab().to_binary(2048 / 8);
221   auto key_id = calc_key_id(key);
222   return std::pair<int64, string>(key_id, std::move(key));
223 }
224 
calc_key_id(Slice auth_key)225 int64 DhHandshake::calc_key_id(Slice auth_key) {
226   UInt<160> auth_key_sha1;
227   sha1(auth_key, auth_key_sha1.raw);
228   return as<int64>(auth_key_sha1.raw + 12);
229 }
230 
231 }  // namespace mtproto
232 }  // namespace td
233