Add DhHandshake::check_config method.
GitOrigin-RevId: 1f9bbd0539454c2e6acbfe3be514614985a2d713
This commit is contained in:
parent
6c45d2c479
commit
466ebd2908
@ -134,10 +134,14 @@ void RSA::decrypt(Slice from, MutableSlice to) const {
|
|||||||
std::memcpy(to.data(), result.c_str(), 256);
|
std::memcpy(to.data(), result.c_str(), 256);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** DH ***/
|
Status DhHandshake::check_config(Slice prime_str, const BigNum &prime, int32 g_int, BigNumContext &ctx,
|
||||||
Status DhHandshake::dh_check(Slice prime_str, const BigNum &prime, int32 g_int, const BigNum &g_a, const BigNum &g_b,
|
DhCallback *callback) {
|
||||||
BigNumContext &ctx, DhCallback *callback) {
|
// check that 2^2047 <= p < 2^2048
|
||||||
// 2. g generates a cyclic subgroup of prime order (p - 1) / 2, i.e. is a quadratic residue mod p.
|
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,
|
// 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
|
// yielding a simple condition on
|
||||||
// * p mod 4g - namely, p mod 8 = 7 for g = 2; p mod 3 = 2 for g = 3;
|
// * p mod 4g - namely, p mod 8 = 7 for g = 2; p mod 3 = 2 for g = 3;
|
||||||
@ -174,40 +178,7 @@ Status DhHandshake::dh_check(Slice prime_str, const BigNum &prime, int32 g_int,
|
|||||||
return Status::Error("Bad prime mod 4g");
|
return Status::Error("Bad prime mod 4g");
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMPORTANT: Apart from the conditions on the Diffie-Hellman prime dh_prime and generator g, both sides are
|
// check whether p is a safe prime (meaning that both p and (p - 1) / 2 are prime)
|
||||||
// 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 that 2^2047 <= p < 2^2048
|
|
||||||
if (prime.get_num_bits() != 2048) {
|
|
||||||
return Status::Error("p is not 2048-bit number");
|
|
||||||
}
|
|
||||||
|
|
||||||
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}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// check whether p = dh_prime is a safe 2048-bit prime (meaning that both p and (p - 1) / 2 are prime)
|
|
||||||
int is_good_prime = -1;
|
int is_good_prime = -1;
|
||||||
if (callback) {
|
if (callback) {
|
||||||
is_good_prime = callback->is_good_prime(prime_str);
|
is_good_prime = callback->is_good_prime(prime_str);
|
||||||
@ -234,21 +205,41 @@ Status DhHandshake::dh_check(Slice prime_str, const BigNum &prime, int32 g_int,
|
|||||||
if (callback) {
|
if (callback) {
|
||||||
callback->add_good_prime(prime_str);
|
callback->add_good_prime(prime_str);
|
||||||
}
|
}
|
||||||
|
return Status::OK();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(perf):
|
Status DhHandshake::dh_check(Slice prime_str, const BigNum &prime, int32 g_int, const BigNum &g_a, const BigNum &g_b,
|
||||||
// Checks:
|
BigNumContext &ctx, DhCallback *callback) {
|
||||||
// After g and p have been checked by the client, it makes sense to cache the result,
|
TRY_STATUS(check_config(prime_str, prime, g_int, ctx, callback));
|
||||||
// so as not to repeat lengthy computations in future.
|
|
||||||
|
|
||||||
// If the verification takes too long time (which is the case for older mobile devices),
|
// IMPORTANT: Apart from the conditions on the Diffie-Hellman prime dh_prime and generator g, both sides are
|
||||||
// one might initially run only 15 Miller-Rabin iterations for verifying primeness of p and (p - 1)/2
|
// to check that g, g_a and g_b are greater than 1 and less than dh_prime - 1.
|
||||||
// with error probability not exceeding one billionth, and do more iterations later in the background.
|
// We recommend checking that g_a and g_b are between 2^{2048-64} and dh_prime - 2^{2048-64} as well.
|
||||||
|
|
||||||
// Another optimization is to embed into the client application code a small table with some known "good"
|
CHECK(prime.get_num_bits() == 2048);
|
||||||
// couples (g,p) (or just known safe primes p, since the condition on g is easily verified during execution),
|
BigNum left;
|
||||||
// checked during code generation phase, so as to avoid doing such verification during runtime altogether.
|
left.set_value(0);
|
||||||
// Server changes these values rarely, thus one usually has to put the current value of server's dh_prime
|
left.set_bit(2048 - 64);
|
||||||
// into such a table. For example, current value of dh_prime equals (in big-endian byte order) ...
|
|
||||||
|
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();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
@ -276,6 +267,12 @@ void DhHandshake::set_config(int32 g_int, Slice prime_str) {
|
|||||||
BigNum::mod_exp(g_b_, g_, b_, prime_, ctx_);
|
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) {
|
void DhHandshake::set_g_a_hash(Slice g_a_hash) {
|
||||||
has_g_a_hash_ = true;
|
has_g_a_hash_ = true;
|
||||||
ok_g_a_hash_ = false;
|
ok_g_a_hash_ = false;
|
||||||
|
@ -60,6 +60,8 @@ class DhHandshake {
|
|||||||
public:
|
public:
|
||||||
void set_config(int32 g_int, Slice prime_str);
|
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 {
|
bool has_config() const {
|
||||||
return has_config_;
|
return has_config_;
|
||||||
}
|
}
|
||||||
@ -133,6 +135,8 @@ class DhHandshake {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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(Slice prime_str, const BigNum &prime, int32 g_int, const BigNum &g_a, const BigNum &g_b,
|
static Status dh_check(Slice prime_str, const BigNum &prime, int32 g_int, const BigNum &g_a, const BigNum &g_b,
|
||||||
BigNumContext &ctx, DhCallback *callback) TD_WARN_UNUSED_RESULT;
|
BigNumContext &ctx, DhCallback *callback) TD_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user