mtproto 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/aes_ige/extconf.rb +3 -9
- data/ext/factorization/extconf.rb +2 -0
- data/lib/mtproto/auth_key_generator.rb +68 -105
- data/lib/mtproto/binary.rb +21 -0
- data/lib/mtproto/client/api/check_password.rb +41 -0
- data/lib/mtproto/client/api/export_login_token.rb +27 -0
- data/lib/mtproto/client/api/get_updates_difference.rb +21 -0
- data/lib/mtproto/client/api/get_updates_state.rb +14 -0
- data/lib/mtproto/client/api/get_users.rb +14 -0
- data/lib/mtproto/client/api/import_login_token.rb +23 -0
- data/lib/mtproto/client/api/send_code.rb +21 -0
- data/lib/mtproto/client/api/sign_in.rb +27 -0
- data/lib/mtproto/client/api.rb +28 -0
- data/lib/mtproto/client/rpc/response.rb +63 -0
- data/lib/mtproto/client/rpc.rb +61 -125
- data/lib/mtproto/client.rb +142 -31
- data/lib/mtproto/crypto/dh_key_exchange.rb +1 -2
- data/lib/mtproto/crypto/dh_validator.rb +17 -19
- data/lib/mtproto/crypto/factorization.rb +1 -1
- data/lib/mtproto/crypto/rsa_key.rb +2 -2
- data/lib/mtproto/crypto/srp.rb +117 -0
- data/lib/mtproto/delegate_methods.rb +11 -0
- data/lib/mtproto/message/message.rb +85 -0
- data/lib/mtproto/session.rb +1 -1
- data/lib/mtproto/tl/constructors.rb +2269 -0
- data/lib/mtproto/tl/object.rb +25 -0
- data/lib/mtproto/tl/objects/account_password.rb +72 -0
- data/lib/mtproto/tl/objects/authorization.rb +73 -0
- data/lib/mtproto/tl/objects/check_password.rb +46 -0
- data/lib/mtproto/tl/objects/client_dh_inner_data.rb +47 -0
- data/lib/mtproto/tl/objects/dh_gen_response.rb +50 -0
- data/lib/mtproto/tl/objects/export_login_token.rb +51 -0
- data/lib/mtproto/tl/objects/get_config.rb +15 -0
- data/lib/mtproto/tl/objects/get_difference.rb +36 -0
- data/lib/mtproto/tl/objects/get_password.rb +15 -0
- data/lib/mtproto/tl/objects/get_state.rb +15 -0
- data/lib/mtproto/tl/objects/get_users.rb +20 -0
- data/lib/mtproto/tl/objects/help_config.rb +77 -0
- data/lib/mtproto/tl/objects/import_login_token.rb +39 -0
- data/lib/mtproto/tl/objects/init_connection.rb +59 -0
- data/lib/mtproto/tl/objects/invoke_with_layer.rb +22 -0
- data/lib/mtproto/tl/objects/login_token.rb +82 -0
- data/lib/mtproto/tl/objects/pq_inner_data.rb +69 -0
- data/lib/mtproto/tl/objects/req_dh_params.rb +65 -0
- data/lib/mtproto/tl/objects/req_pq_multi.rb +23 -0
- data/lib/mtproto/tl/objects/res_pq.rb +75 -0
- data/lib/mtproto/tl/objects/send_code.rb +50 -0
- data/lib/mtproto/tl/objects/sent_code.rb +79 -0
- data/lib/mtproto/tl/objects/server_dh_inner_data.rb +74 -0
- data/lib/mtproto/tl/objects/server_dh_params.rb +53 -0
- data/lib/mtproto/tl/objects/set_client_dh_params.rb +48 -0
- data/lib/mtproto/tl/objects/sign_in.rb +47 -0
- data/lib/mtproto/tl/objects/update.rb +80 -0
- data/lib/mtproto/tl/objects/update_short.rb +22 -0
- data/lib/mtproto/tl/objects/update_short_message.rb +67 -0
- data/lib/mtproto/tl/objects/updates_difference.rb +157 -0
- data/lib/mtproto/tl/objects/updates_state.rb +37 -0
- data/lib/mtproto/tl/objects/users.rb +86 -0
- data/lib/mtproto/transport/abridged_packet_codec.rb +35 -12
- data/lib/mtproto/transport/connection.rb +23 -0
- data/lib/mtproto/transport/errors.rb +11 -0
- data/lib/mtproto/transport/packet.rb +19 -0
- data/lib/mtproto/transport/tcp_connection.rb +57 -46
- data/lib/mtproto/type/bad_msg_notification.rb +10 -10
- data/lib/mtproto/type/gzip_packed.rb +5 -3
- data/lib/mtproto/type/message.rb +2 -2
- data/lib/mtproto/type/rpc_error.rb +0 -1
- data/lib/mtproto/updates_poller.rb +37 -33
- data/lib/mtproto/version.rb +1 -1
- data/lib/mtproto.rb +11 -17
- data/scripts/generate_constructors.rb +65 -0
- metadata +62 -51
- data/lib/mtproto/async/middleware/base.rb +0 -17
- data/lib/mtproto/async/middleware/flood_wait.rb +0 -42
- data/lib/mtproto/async/request.rb +0 -18
- data/lib/mtproto/async/request_queue.rb +0 -63
- data/lib/mtproto/async_client.rb +0 -201
- data/lib/mtproto/rpc/get_config.rb +0 -34
- data/lib/mtproto/rpc/get_contacts.rb +0 -29
- data/lib/mtproto/rpc/get_updates_difference.rb +0 -51
- data/lib/mtproto/rpc/get_updates_state.rb +0 -29
- data/lib/mtproto/rpc/get_users.rb +0 -29
- data/lib/mtproto/rpc/ping.rb +0 -33
- data/lib/mtproto/rpc/send_code.rb +0 -41
- data/lib/mtproto/rpc/send_message.rb +0 -47
- data/lib/mtproto/rpc/sign_in.rb +0 -48
- data/lib/mtproto/type/auth_key/dh_gen_response.rb +0 -37
- data/lib/mtproto/type/auth_key/req_dh_params.rb +0 -31
- data/lib/mtproto/type/auth_key/req_pq_multi.rb +0 -18
- data/lib/mtproto/type/auth_key/res_pq.rb +0 -62
- data/lib/mtproto/type/auth_key/server_dh_params.rb +0 -43
- data/lib/mtproto/type/auth_key/set_client_dh_params.rb +0 -25
- data/lib/mtproto/type/code_settings.rb +0 -25
- data/lib/mtproto/type/config.rb +0 -124
- data/lib/mtproto/type/rpc/auth/authorization.rb +0 -107
- data/lib/mtproto/type/rpc/auth/send_code.rb +0 -28
- data/lib/mtproto/type/rpc/auth/sent_code.rb +0 -36
- data/lib/mtproto/type/rpc/auth/sign_in.rb +0 -32
- data/lib/mtproto/type/rpc/contacts/contacts.rb +0 -155
- data/lib/mtproto/type/rpc/contacts/get_contacts.rb +0 -18
- data/lib/mtproto/type/rpc/help/config.rb +0 -35
- data/lib/mtproto/type/rpc/help/get_config.rb +0 -17
- data/lib/mtproto/type/rpc/init_connection.rb +0 -28
- data/lib/mtproto/type/rpc/invoke_with_layer.rb +0 -19
- data/lib/mtproto/type/rpc/messages/send_message.rb +0 -43
- data/lib/mtproto/type/rpc/messages/updates.rb +0 -87
- data/lib/mtproto/type/rpc/ping.rb +0 -18
- data/lib/mtproto/type/rpc/pong.rb +0 -46
- data/lib/mtproto/type/rpc/updates/difference.rb +0 -332
- data/lib/mtproto/type/rpc/updates/get_difference.rb +0 -42
- data/lib/mtproto/type/rpc/updates/get_state.rb +0 -17
- data/lib/mtproto/type/rpc/updates/state.rb +0 -59
- data/lib/mtproto/type/rpc/users/get_users.rb +0 -25
- data/lib/mtproto/type/rpc/users/users.rb +0 -99
- data/lib/mtproto/type/sent_code.rb +0 -128
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 46c1ad0b399a4ecb8502c4783d7a032be25a502620357a0a04009d01ae3feb48
|
|
4
|
+
data.tar.gz: 28f124987f99bb50e8fa4cc6a9f00b77a926b7285ab159039987369ab1359a88
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 89d8dbe1f183814f2a6f9b485d85f0ae6b92c4895aae6c2493a41a54a740090af30910c0086de0e82d53415905e32a7a2a8b0273eff66393c594ae5ebe1bee1b
|
|
7
|
+
data.tar.gz: 31793f4d495ccceb6a18959261673e8d3c0388ff9bd02f8bf848ea093dcedde7457412763aeaa87f942251c49906dcd7b39bb9d03b3ade3f64d4b17a8df433f5
|
data/ext/aes_ige/extconf.rb
CHANGED
|
@@ -6,18 +6,12 @@ require 'mkmf'
|
|
|
6
6
|
dir_config('openssl')
|
|
7
7
|
|
|
8
8
|
# Check for OpenSSL headers and libraries
|
|
9
|
-
unless have_header('openssl/aes.h')
|
|
10
|
-
abort 'OpenSSL headers not found. Please install OpenSSL development files.'
|
|
11
|
-
end
|
|
9
|
+
abort 'OpenSSL headers not found. Please install OpenSSL development files.' unless have_header('openssl/aes.h')
|
|
12
10
|
|
|
13
|
-
unless have_library('ssl') && have_library('crypto')
|
|
14
|
-
abort 'OpenSSL libraries not found. Please install OpenSSL.'
|
|
15
|
-
end
|
|
11
|
+
abort 'OpenSSL libraries not found. Please install OpenSSL.' unless have_library('ssl') && have_library('crypto')
|
|
16
12
|
|
|
17
13
|
# Check for AES_ige_encrypt function
|
|
18
|
-
unless have_func('AES_ige_encrypt', 'openssl/aes.h')
|
|
19
|
-
abort 'AES_ige_encrypt function not found in OpenSSL library.'
|
|
20
|
-
end
|
|
14
|
+
abort 'AES_ige_encrypt function not found in OpenSSL library.' unless have_func('AES_ige_encrypt', 'openssl/aes.h')
|
|
21
15
|
|
|
22
16
|
# Suppress deprecation warnings for OpenSSL 3.0
|
|
23
17
|
$CFLAGS += ' -Wno-deprecated-declarations'
|
|
@@ -2,21 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
require 'securerandom'
|
|
4
4
|
require 'digest'
|
|
5
|
-
require_relative '
|
|
6
|
-
require_relative '
|
|
7
|
-
require_relative '
|
|
8
|
-
require_relative '
|
|
9
|
-
require_relative '
|
|
10
|
-
require_relative '
|
|
11
|
-
require_relative '
|
|
5
|
+
require_relative 'tl/objects/req_pq_multi'
|
|
6
|
+
require_relative 'tl/objects/res_pq'
|
|
7
|
+
require_relative 'tl/objects/pq_inner_data'
|
|
8
|
+
require_relative 'tl/objects/req_dh_params'
|
|
9
|
+
require_relative 'tl/objects/server_dh_params'
|
|
10
|
+
require_relative 'tl/objects/server_dh_inner_data'
|
|
11
|
+
require_relative 'tl/objects/client_dh_inner_data'
|
|
12
|
+
require_relative 'tl/objects/set_client_dh_params'
|
|
13
|
+
require_relative 'tl/objects/dh_gen_response'
|
|
12
14
|
|
|
13
15
|
module MTProto
|
|
14
16
|
class AuthKeyGenerator
|
|
15
17
|
attr_reader :connection, :auth_key, :server_salt, :time_offset, :timeout
|
|
16
18
|
|
|
17
19
|
def initialize(connection, public_key, dc_number = nil, test_mode: false, timeout: 10)
|
|
18
|
-
raise ArgumentError,
|
|
19
|
-
raise ArgumentError,
|
|
20
|
+
raise ArgumentError, 'public_key is required' if public_key.nil? || public_key.empty?
|
|
21
|
+
raise ArgumentError, 'dc_number must be positive. Use test_mode: true for test DCs' if dc_number && dc_number < 0
|
|
20
22
|
|
|
21
23
|
@connection = connection
|
|
22
24
|
@public_key = public_key
|
|
@@ -29,13 +31,15 @@ module MTProto
|
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
def generate
|
|
32
|
-
res_pq =
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
res_pq = send_and_receive(TL::ReqPqMulti.new(SecureRandom.random_bytes(16)), TL::ResPq)
|
|
35
|
+
|
|
36
|
+
server_key = Crypto::RSAKey.find_by_fingerprint(res_pq.fingerprints, @public_key)
|
|
37
|
+
raise 'No matching RSA key found!' unless server_key
|
|
38
|
+
|
|
39
|
+
p, q = Crypto::Factorization.factorize_pq(res_pq.pq)
|
|
35
40
|
new_nonce = SecureRandom.random_bytes(32)
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
server_dh_params = send_req_dh_params(res_pq, p, q, server_key, encrypted_data)
|
|
42
|
+
server_dh_params = send_req_dh_params(res_pq, p, q, server_key, new_nonce)
|
|
39
43
|
server_dh_inner_data = decrypt_server_dh_params(res_pq, new_nonce, server_dh_params)
|
|
40
44
|
|
|
41
45
|
Crypto::DHValidator.validate_dh_params(
|
|
@@ -55,8 +59,8 @@ module MTProto
|
|
|
55
59
|
server_dh_inner_data.dh_prime
|
|
56
60
|
)
|
|
57
61
|
|
|
58
|
-
tmp_aes_key = Crypto::AuthKeyHelper.derive_tmp_aes_key(new_nonce, res_pq
|
|
59
|
-
tmp_aes_iv = Crypto::AuthKeyHelper.derive_tmp_aes_iv(new_nonce, res_pq
|
|
62
|
+
tmp_aes_key = Crypto::AuthKeyHelper.derive_tmp_aes_key(new_nonce, res_pq.server_nonce)
|
|
63
|
+
tmp_aes_iv = Crypto::AuthKeyHelper.derive_tmp_aes_iv(new_nonce, res_pq.server_nonce)
|
|
60
64
|
|
|
61
65
|
dh_gen_response = send_client_dh_params(
|
|
62
66
|
res_pq,
|
|
@@ -66,96 +70,69 @@ module MTProto
|
|
|
66
70
|
tmp_aes_iv
|
|
67
71
|
)
|
|
68
72
|
|
|
69
|
-
verify_dh_gen_response(dh_gen_response,
|
|
73
|
+
verify_dh_gen_response(dh_gen_response, new_nonce, auth_key)
|
|
70
74
|
|
|
71
75
|
@auth_key = auth_key
|
|
72
|
-
@server_salt = compute_server_salt(new_nonce, res_pq
|
|
76
|
+
@server_salt = compute_server_salt(new_nonce, res_pq.server_nonce)
|
|
73
77
|
@time_offset = server_dh_inner_data.server_time - Time.now.to_i
|
|
74
78
|
|
|
75
79
|
{
|
|
76
80
|
auth_key: @auth_key,
|
|
77
81
|
server_salt: @server_salt,
|
|
78
|
-
time_offset: @time_offset
|
|
79
|
-
server_dh_params: server_dh_params,
|
|
80
|
-
server_dh_inner_data: server_dh_inner_data,
|
|
81
|
-
client_dh_params: client_dh_params,
|
|
82
|
-
dh_gen_response: dh_gen_response
|
|
82
|
+
time_offset: @time_offset
|
|
83
83
|
}
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
private
|
|
87
87
|
|
|
88
|
-
def
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
message = Type::Message.new(auth_key_id: 0, msg_id: MessageId.generate, body: body)
|
|
92
|
-
@connection.send(message.serialize)
|
|
88
|
+
def send_and_receive(rpc, response_class)
|
|
89
|
+
message = Message.new(rpc.body)
|
|
90
|
+
@connection.send(Transport::Packet.new(message.bytes))
|
|
93
91
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
res_pq = Type::AuthKey::ResPq.parse(response_message.body)
|
|
92
|
+
response_packet = @connection.receive
|
|
93
|
+
raise 'Receive timeout' if response_packet.nil?
|
|
97
94
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
res_pq
|
|
101
|
-
end
|
|
95
|
+
result = response_class.parse(Message.parse(response_packet))
|
|
102
96
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
raise 'No matching RSA key found!' unless server_key
|
|
97
|
+
raise 'Nonce mismatch!' if rpc.respond_to?(:nonce) && result.nonce != rpc.nonce
|
|
98
|
+
raise 'Server nonce mismatch!' if rpc.respond_to?(:server_nonce) && result.server_nonce != rpc.server_nonce
|
|
106
99
|
|
|
107
|
-
|
|
100
|
+
result
|
|
108
101
|
end
|
|
109
102
|
|
|
110
|
-
def
|
|
111
|
-
raise ArgumentError,
|
|
103
|
+
def send_req_dh_params(res_pq, p, q, server_key, new_nonce)
|
|
104
|
+
raise ArgumentError, 'dc_number is required for auth key generation' if @dc_number.nil?
|
|
112
105
|
|
|
113
|
-
|
|
114
|
-
# For production DCs, use the DC number as-is
|
|
115
|
-
dc_value = @test_mode ? (@dc_number + 10000) : @dc_number
|
|
106
|
+
dc_value = @test_mode ? (@dc_number + 10_000) : @dc_number
|
|
116
107
|
|
|
117
|
-
inner_data =
|
|
118
|
-
pq: Crypto::Factorization.bytes_to_integer(res_pq
|
|
108
|
+
inner_data = TL::PQInnerData.new(
|
|
109
|
+
pq: Crypto::Factorization.bytes_to_integer(res_pq.pq),
|
|
119
110
|
p: p,
|
|
120
111
|
q: q,
|
|
121
|
-
nonce: res_pq
|
|
122
|
-
server_nonce: res_pq
|
|
112
|
+
nonce: res_pq.nonce,
|
|
113
|
+
server_nonce: res_pq.server_nonce,
|
|
123
114
|
new_nonce: new_nonce,
|
|
124
115
|
dc: dc_value
|
|
125
116
|
)
|
|
117
|
+
encrypted_data = Crypto::RSA_PAD.encrypt(inner_data.body.pack('C*'), server_key)
|
|
126
118
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def send_req_dh_params(res_pq, p, q, server_key, encrypted_data)
|
|
131
|
-
body = Type::AuthKey::ReqDHParams.build(
|
|
132
|
-
nonce: res_pq[:nonce],
|
|
133
|
-
server_nonce: res_pq[:server_nonce],
|
|
119
|
+
rpc = TL::ReqDHParams.new(
|
|
120
|
+
nonce: res_pq.nonce,
|
|
121
|
+
server_nonce: res_pq.server_nonce,
|
|
134
122
|
p: p,
|
|
135
123
|
q: q,
|
|
136
124
|
public_key_fingerprint: server_key.fingerprint,
|
|
137
125
|
encrypted_data: encrypted_data
|
|
138
126
|
)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
@connection.send(message.serialize)
|
|
142
|
-
|
|
143
|
-
response_data = @connection.recv(timeout: @timeout)
|
|
144
|
-
response_message = Type::Message.deserialize(response_data)
|
|
145
|
-
server_dh_params = Type::AuthKey::ServerDHParams.parse(response_message.body)
|
|
146
|
-
|
|
147
|
-
raise 'Nonce mismatch!' unless server_dh_params[:nonce] == res_pq[:nonce]
|
|
148
|
-
raise 'Server nonce mismatch!' unless server_dh_params[:server_nonce] == res_pq[:server_nonce]
|
|
149
|
-
|
|
150
|
-
server_dh_params
|
|
127
|
+
send_and_receive(rpc, TL::ServerDHParams)
|
|
151
128
|
end
|
|
152
129
|
|
|
153
130
|
def decrypt_server_dh_params(res_pq, new_nonce, server_dh_params)
|
|
154
|
-
tmp_aes_key = Crypto::AuthKeyHelper.derive_tmp_aes_key(new_nonce, res_pq
|
|
155
|
-
tmp_aes_iv = Crypto::AuthKeyHelper.derive_tmp_aes_iv(new_nonce, res_pq
|
|
131
|
+
tmp_aes_key = Crypto::AuthKeyHelper.derive_tmp_aes_key(new_nonce, res_pq.server_nonce)
|
|
132
|
+
tmp_aes_iv = Crypto::AuthKeyHelper.derive_tmp_aes_iv(new_nonce, res_pq.server_nonce)
|
|
156
133
|
|
|
157
134
|
answer_with_hash = Crypto::AES_IGE.decrypt_ige(
|
|
158
|
-
server_dh_params
|
|
135
|
+
server_dh_params.encrypted_answer,
|
|
159
136
|
tmp_aes_key,
|
|
160
137
|
tmp_aes_iv
|
|
161
138
|
)
|
|
@@ -172,23 +149,18 @@ module MTProto
|
|
|
172
149
|
answer = answer[0..-2]
|
|
173
150
|
end
|
|
174
151
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
raise 'Nonce mismatch in DH inner data!' unless server_dh_inner_data.nonce == res_pq[:nonce]
|
|
178
|
-
raise 'Server nonce mismatch in DH inner data!' unless server_dh_inner_data.server_nonce == res_pq[:server_nonce]
|
|
179
|
-
|
|
180
|
-
server_dh_inner_data
|
|
152
|
+
TL::ServerDHInnerData.parse(answer)
|
|
181
153
|
end
|
|
182
154
|
|
|
183
|
-
def send_client_dh_params(res_pq,
|
|
184
|
-
client_dh_inner_data =
|
|
185
|
-
nonce: res_pq
|
|
186
|
-
server_nonce: res_pq
|
|
155
|
+
def send_client_dh_params(res_pq, _new_nonce, client_dh_params, tmp_aes_key, tmp_aes_iv)
|
|
156
|
+
client_dh_inner_data = TL::ClientDHInnerData.new(
|
|
157
|
+
nonce: res_pq.nonce,
|
|
158
|
+
server_nonce: res_pq.server_nonce,
|
|
187
159
|
retry_id: 0,
|
|
188
160
|
g_b: client_dh_params[:g_b_bytes]
|
|
189
161
|
)
|
|
190
162
|
|
|
191
|
-
client_dh_data = client_dh_inner_data.
|
|
163
|
+
client_dh_data = client_dh_inner_data.body.pack('C*')
|
|
192
164
|
client_dh_data_with_hash = Digest::SHA1.digest(client_dh_data) + client_dh_data
|
|
193
165
|
|
|
194
166
|
padding_length = (16 - (client_dh_data_with_hash.bytesize % 16)) % 16
|
|
@@ -200,39 +172,30 @@ module MTProto
|
|
|
200
172
|
tmp_aes_iv
|
|
201
173
|
)
|
|
202
174
|
|
|
203
|
-
|
|
204
|
-
nonce: res_pq
|
|
205
|
-
server_nonce: res_pq
|
|
175
|
+
rpc = TL::SetClientDHParams.new(
|
|
176
|
+
nonce: res_pq.nonce,
|
|
177
|
+
server_nonce: res_pq.server_nonce,
|
|
206
178
|
encrypted_data: client_dh_encrypted
|
|
207
179
|
)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
@connection.send(message.serialize)
|
|
211
|
-
|
|
212
|
-
response_data = @connection.recv(timeout: @timeout)
|
|
213
|
-
response_message = Type::Message.deserialize(response_data)
|
|
214
|
-
Type::AuthKey::DHGenResponse.parse(response_message.body)
|
|
180
|
+
send_and_receive(rpc, TL::DHGenResponse)
|
|
215
181
|
end
|
|
216
182
|
|
|
217
|
-
def verify_dh_gen_response(dh_gen_response,
|
|
218
|
-
raise 'Nonce mismatch in DH response!' unless dh_gen_response[:nonce] == res_pq[:nonce]
|
|
219
|
-
raise 'Server nonce mismatch in DH response!' unless dh_gen_response[:server_nonce] == res_pq[:server_nonce]
|
|
220
|
-
|
|
183
|
+
def verify_dh_gen_response(dh_gen_response, new_nonce, auth_key)
|
|
221
184
|
auth_key_hash = Digest::SHA1.digest(auth_key)
|
|
222
185
|
auth_key_aux_hash = auth_key_hash[0, 8]
|
|
223
186
|
|
|
224
|
-
expected_new_nonce_hash = case dh_gen_response
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
187
|
+
expected_new_nonce_hash = case dh_gen_response.status
|
|
188
|
+
when :ok
|
|
189
|
+
Digest::SHA1.digest(new_nonce + "\x01".b + auth_key_aux_hash)[-16..]
|
|
190
|
+
when :retry
|
|
191
|
+
Digest::SHA1.digest(new_nonce + "\x02".b + auth_key_aux_hash)[-16..]
|
|
192
|
+
when :fail
|
|
193
|
+
Digest::SHA1.digest(new_nonce + "\x03".b + auth_key_aux_hash)[-16..]
|
|
194
|
+
end
|
|
232
195
|
|
|
233
|
-
raise 'new_nonce_hash mismatch!' unless dh_gen_response
|
|
196
|
+
raise 'new_nonce_hash mismatch!' unless dh_gen_response.new_nonce_hash == expected_new_nonce_hash
|
|
234
197
|
|
|
235
|
-
case dh_gen_response
|
|
198
|
+
case dh_gen_response.status
|
|
236
199
|
when :retry
|
|
237
200
|
raise 'Server requested retry - not implemented'
|
|
238
201
|
when :fail
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module Binary
|
|
5
|
+
def b_u32(bytes)
|
|
6
|
+
bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def b_u64(bytes)
|
|
10
|
+
b_u32(bytes[0, 4]) | (b_u32(bytes[4, 4]) << 32)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def u32_b(value)
|
|
14
|
+
[value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, (value >> 24) & 0xff]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def u64_b(value)
|
|
18
|
+
u32_b(value & 0xffffffff) + u32_b(value >> 32)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../tl/objects/get_password'
|
|
4
|
+
require_relative '../../tl/objects/check_password'
|
|
5
|
+
require_relative '../../tl/objects/account_password'
|
|
6
|
+
require_relative '../../tl/objects/authorization'
|
|
7
|
+
require_relative '../../crypto/srp'
|
|
8
|
+
|
|
9
|
+
module MTProto
|
|
10
|
+
class Client
|
|
11
|
+
class API
|
|
12
|
+
def check_password(password:)
|
|
13
|
+
pwd = rpc_call(TL::GetPassword.new, TL::AccountPassword).body
|
|
14
|
+
|
|
15
|
+
raise 'Account does not have 2FA enabled' unless pwd.has_password
|
|
16
|
+
|
|
17
|
+
srp_result = Crypto::SRP.compute_check(
|
|
18
|
+
algo: pwd.current_algo,
|
|
19
|
+
srp_b: pwd.srp_b,
|
|
20
|
+
srp_id: pwd.srp_id,
|
|
21
|
+
password: password
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
result = rpc_call(
|
|
25
|
+
TL::CheckPassword.new(
|
|
26
|
+
srp_id: srp_result[:srp_id],
|
|
27
|
+
a: srp_result[:a],
|
|
28
|
+
m1: srp_result[:m1]
|
|
29
|
+
),
|
|
30
|
+
TL::Authorization
|
|
31
|
+
).body
|
|
32
|
+
|
|
33
|
+
if result.authorization? && result.user_id
|
|
34
|
+
@client.update_user(user_id: result.user_id, access_hash: result.access_hash)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
result
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../tl/objects/export_login_token'
|
|
4
|
+
require_relative '../../tl/objects/login_token'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
class Client
|
|
8
|
+
class API
|
|
9
|
+
def export_login_token(except_ids: [])
|
|
10
|
+
result = rpc_call(
|
|
11
|
+
TL::ExportLoginToken.new(
|
|
12
|
+
api_id: @client.api_id,
|
|
13
|
+
api_hash: @client.api_hash,
|
|
14
|
+
except_ids: except_ids
|
|
15
|
+
),
|
|
16
|
+
TL::LoginToken
|
|
17
|
+
).body
|
|
18
|
+
|
|
19
|
+
if result.success? && result.user_id
|
|
20
|
+
@client.update_user(user_id: result.user_id, access_hash: result.access_hash)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
result
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../tl/objects/get_difference'
|
|
4
|
+
require_relative '../../tl/objects/updates_difference'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
class Client
|
|
8
|
+
class API
|
|
9
|
+
def get_updates_difference(pts:, date:, qts:, pts_limit: nil, qts_limit: nil, pts_total_limit: nil)
|
|
10
|
+
rpc_call(
|
|
11
|
+
TL::GetDifference.new(
|
|
12
|
+
pts: pts, date: date, qts: qts,
|
|
13
|
+
pts_limit: pts_limit, qts_limit: qts_limit,
|
|
14
|
+
pts_total_limit: pts_total_limit
|
|
15
|
+
),
|
|
16
|
+
TL::UpdatesDifference
|
|
17
|
+
).body
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../tl/objects/get_state'
|
|
4
|
+
require_relative '../../tl/objects/updates_state'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
class Client
|
|
8
|
+
class API
|
|
9
|
+
def get_updates_state
|
|
10
|
+
rpc_call(TL::GetState.new, TL::UpdatesState).body
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../tl/objects/get_users'
|
|
4
|
+
require_relative '../../tl/objects/users'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
class Client
|
|
8
|
+
class API
|
|
9
|
+
def get_users
|
|
10
|
+
rpc_call(TL::GetUsers.new, TL::Users).body
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../tl/objects/import_login_token'
|
|
4
|
+
require_relative '../../tl/objects/login_token'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
class Client
|
|
8
|
+
class API
|
|
9
|
+
def import_login_token(token:)
|
|
10
|
+
result = rpc_call(
|
|
11
|
+
TL::ImportLoginToken.new(token: token),
|
|
12
|
+
TL::LoginToken
|
|
13
|
+
).body
|
|
14
|
+
|
|
15
|
+
if result.success? && result.user_id
|
|
16
|
+
@client.update_user(user_id: result.user_id, access_hash: result.access_hash)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
result
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../tl/objects/send_code'
|
|
4
|
+
require_relative '../../tl/objects/sent_code'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
class Client
|
|
8
|
+
class API
|
|
9
|
+
def send_code(phone_number)
|
|
10
|
+
rpc_call(
|
|
11
|
+
TL::SendCode.new(
|
|
12
|
+
phone_number: phone_number,
|
|
13
|
+
api_id: @client.api_id,
|
|
14
|
+
api_hash: @client.api_hash
|
|
15
|
+
),
|
|
16
|
+
TL::SentCode
|
|
17
|
+
).body
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../tl/objects/sign_in'
|
|
4
|
+
require_relative '../../tl/objects/authorization'
|
|
5
|
+
|
|
6
|
+
module MTProto
|
|
7
|
+
class Client
|
|
8
|
+
class API
|
|
9
|
+
def sign_in(phone_number:, phone_code_hash:, phone_code:)
|
|
10
|
+
result = rpc_call(
|
|
11
|
+
TL::SignIn.new(
|
|
12
|
+
phone_number: phone_number,
|
|
13
|
+
phone_code_hash: phone_code_hash,
|
|
14
|
+
phone_code: phone_code
|
|
15
|
+
),
|
|
16
|
+
TL::Authorization
|
|
17
|
+
).body
|
|
18
|
+
|
|
19
|
+
if result.authorization? && result.user_id
|
|
20
|
+
@client.update_user(user_id: result.user_id, access_hash: result.access_hash)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
result
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'api/send_code'
|
|
4
|
+
require_relative 'api/sign_in'
|
|
5
|
+
require_relative 'api/export_login_token'
|
|
6
|
+
require_relative 'api/import_login_token'
|
|
7
|
+
require_relative 'api/check_password'
|
|
8
|
+
require_relative 'api/get_users'
|
|
9
|
+
require_relative 'api/get_updates_state'
|
|
10
|
+
require_relative 'api/get_updates_difference'
|
|
11
|
+
|
|
12
|
+
module MTProto
|
|
13
|
+
class Client
|
|
14
|
+
class API
|
|
15
|
+
def initialize(client)
|
|
16
|
+
@client = client
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def rpc_call(request, response_class)
|
|
22
|
+
response = @client.rpc.call(request, response_class)
|
|
23
|
+
response.wait!(@client.timeout)
|
|
24
|
+
response
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'async/condition'
|
|
4
|
+
|
|
5
|
+
module MTProto
|
|
6
|
+
class Client
|
|
7
|
+
class RPC
|
|
8
|
+
class Response
|
|
9
|
+
attr_reader :msg_id, :body_bytes, :response_class
|
|
10
|
+
|
|
11
|
+
def initialize(msg_id, response_class, body_bytes)
|
|
12
|
+
@msg_id = msg_id
|
|
13
|
+
@response_class = response_class
|
|
14
|
+
@body_bytes = body_bytes
|
|
15
|
+
@condition = ::Async::Condition.new
|
|
16
|
+
@result = nil
|
|
17
|
+
@error = nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def received?
|
|
21
|
+
!@result.nil? || !@error.nil?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def wait!(timeout = nil)
|
|
25
|
+
unless received?
|
|
26
|
+
if timeout
|
|
27
|
+
::Async::Task.current.with_timeout(timeout) { receive_signal }
|
|
28
|
+
else
|
|
29
|
+
receive_signal
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
raise @error if @error
|
|
34
|
+
|
|
35
|
+
@result
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def body
|
|
39
|
+
raise 'Response not received yet' unless received?
|
|
40
|
+
raise @error if @error
|
|
41
|
+
|
|
42
|
+
@result
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def signal(raw_body)
|
|
46
|
+
@result = @response_class.parse(raw_body)
|
|
47
|
+
@condition.signal
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def signal_error(error)
|
|
51
|
+
@error = error
|
|
52
|
+
@condition.signal
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def receive_signal
|
|
58
|
+
@condition.wait
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|