summaryrefslogtreecommitdiff
path: root/protocols/Telegram/tdlib/td/td/mtproto/RSA.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Telegram/tdlib/td/td/mtproto/RSA.cpp')
-rw-r--r--protocols/Telegram/tdlib/td/td/mtproto/RSA.cpp158
1 files changed, 158 insertions, 0 deletions
diff --git a/protocols/Telegram/tdlib/td/td/mtproto/RSA.cpp b/protocols/Telegram/tdlib/td/td/mtproto/RSA.cpp
new file mode 100644
index 0000000000..9f32887261
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/td/mtproto/RSA.cpp
@@ -0,0 +1,158 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// 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/RSA.h"
+
+#include "td/mtproto/mtproto_api.h"
+
+#include "td/utils/as.h"
+#include "td/utils/common.h"
+#include "td/utils/crypto.h"
+#include "td/utils/misc.h"
+#include "td/utils/ScopeGuard.h"
+#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
+#include "td/utils/tl_storers.h"
+
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/opensslv.h>
+#include <openssl/pem.h>
+#if OPENSSL_VERSION_NUMBER < 0x30000000L || defined(LIBRESSL_VERSION_NUMBER)
+#include <openssl/rsa.h>
+#endif
+
+namespace td {
+namespace mtproto {
+
+RSA::RSA(BigNum n, BigNum e) : n_(std::move(n)), e_(std::move(e)) {
+}
+
+RSA RSA::clone() const {
+ return RSA(n_.clone(), e_.clone());
+}
+
+Result<RSA> RSA::from_pem_public_key(Slice pem) {
+ init_crypto();
+
+ auto *bio =
+ BIO_new_mem_buf(const_cast<void *>(static_cast<const void *>(pem.ubegin())), narrow_cast<int32>(pem.size()));
+ if (bio == nullptr) {
+ return Status::Error("Cannot create BIO");
+ }
+ SCOPE_EXIT {
+ BIO_free(bio);
+ };
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ EVP_PKEY *rsa = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr);
+#else
+ auto rsa = PEM_read_bio_RSAPublicKey(bio, nullptr, nullptr, nullptr);
+#endif
+ if (rsa == nullptr) {
+ return Status::Error("Error while reading RSA public key");
+ }
+ SCOPE_EXIT {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ EVP_PKEY_free(rsa);
+#else
+ RSA_free(rsa);
+#endif
+ };
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ if (!EVP_PKEY_is_a(rsa, "RSA")) {
+ return Status::Error("Key is not an RSA key");
+ }
+ if (EVP_PKEY_size(rsa) != 256) {
+ return Status::Error("EVP_PKEY_size != 256");
+ }
+#else
+ if (RSA_size(rsa) != 256) {
+ return Status::Error("RSA_size != 256");
+ }
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ BIGNUM *n_num = nullptr;
+ BIGNUM *e_num = nullptr;
+
+ int res = EVP_PKEY_get_bn_param(rsa, "n", &n_num);
+ CHECK(res == 1 && n_num != nullptr);
+ res = EVP_PKEY_get_bn_param(rsa, "e", &e_num);
+ CHECK(res == 1 && e_num != nullptr);
+
+ auto n = static_cast<void *>(n_num);
+ auto e = static_cast<void *>(e_num);
+#else
+ const BIGNUM *n_num;
+ const BIGNUM *e_num;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ RSA_get0_key(rsa, &n_num, &e_num, nullptr);
+#else
+ n_num = rsa->n;
+ e_num = rsa->e;
+#endif
+
+ auto n = static_cast<void *>(BN_dup(n_num));
+ auto e = static_cast<void *>(BN_dup(e_num));
+ if (n == nullptr || e == nullptr) {
+ return Status::Error("Cannot dup BIGNUM");
+ }
+#endif
+
+ return RSA(BigNum::from_raw(n), BigNum::from_raw(e));
+}
+
+int64 RSA::get_fingerprint() const {
+ // string objects are necessary, because mtproto_api::rsa_public_key contains Slice inside
+ string n_str = n_.to_binary();
+ string e_str = e_.to_binary();
+ mtproto_api::rsa_public_key public_key(n_str, e_str);
+ size_t size = tl_calc_length(public_key);
+ std::vector<unsigned char> tmp(size);
+ size = tl_store_unsafe(public_key, tmp.data());
+ CHECK(size == tmp.size());
+ unsigned char key_sha1[20];
+ sha1(Slice(tmp.data(), tmp.size()), key_sha1);
+ return as<int64>(key_sha1 + 12);
+}
+
+size_t RSA::size() const {
+ // Checked in RSA::from_pem_public_key step
+ return 256;
+}
+
+bool RSA::encrypt(Slice from, MutableSlice to) const {
+ CHECK(from.size() == 256)
+ CHECK(to.size() == 256)
+ int bits = n_.get_num_bits();
+ CHECK(bits >= 2041 && bits <= 2048);
+
+ BigNum x = BigNum::from_binary(from);
+ if (BigNum::compare(x, n_) >= 0) {
+ return false;
+ }
+
+ BigNumContext ctx;
+ BigNum y;
+ BigNum::mod_exp(y, x, e_, n_, ctx);
+ to.copy_from(y.to_binary(256));
+ return true;
+}
+
+void RSA::decrypt_signature(Slice from, MutableSlice to) const {
+ CHECK(from.size() == 256);
+ BigNumContext ctx;
+ BigNum x = BigNum::from_binary(from);
+ BigNum y;
+ BigNum::mod_exp(y, x, e_, n_, ctx);
+ to.copy_from(y.to_binary(256));
+}
+
+} // namespace mtproto
+} // namespace td