mtproto 0.0.4 → 0.0.5

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.
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'serializer'
4
+
5
+ module MTProto
6
+ module TL
7
+ class ClientDHInnerData
8
+ CONSTRUCTOR = 0x6643b654
9
+
10
+ attr_reader :nonce, :server_nonce, :retry_id, :g_b
11
+
12
+ def initialize(nonce:, server_nonce:, retry_id:, g_b:)
13
+ @nonce = nonce
14
+ @server_nonce = server_nonce
15
+ @retry_id = retry_id
16
+ @g_b = g_b
17
+ end
18
+
19
+ def serialize
20
+ data = Serializer.serialize_int(CONSTRUCTOR)
21
+ data += @nonce
22
+ data += @server_nonce
23
+ data += Serializer.serialize_long(@retry_id)
24
+ data += Serializer.serialize_bytes(@g_b)
25
+ data
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class Config
6
+ CONSTRUCTOR = 0xcc1a241e
7
+ CONSTRUCTOR_ALT = 0x3072cfa1
8
+
9
+ attr_reader :flags, :date, :expires, :test_mode, :this_dc, :dc_options
10
+
11
+ def self.deserialize(data)
12
+ offset = 4
13
+ flags = data[offset, 4].unpack1('L<')
14
+ offset += 4
15
+
16
+ date = data[offset, 4].unpack1('L<')
17
+ offset += 4
18
+
19
+ expires = data[offset, 4].unpack1('L<')
20
+ offset += 4
21
+
22
+ test_mode = (data[offset, 4].unpack1('L<') == 0x997275b5)
23
+ offset += 4
24
+
25
+ this_dc = data[offset, 4].unpack1('L<')
26
+ offset += 4
27
+
28
+ dc_options_constructor = data[offset, 4].unpack1('L<')
29
+ offset += 4
30
+
31
+ raise "Expected vector constructor" unless dc_options_constructor == 0x1cb5c415
32
+
33
+ dc_options_count = data[offset, 4].unpack1('L<')
34
+ offset += 4
35
+
36
+ dc_options = []
37
+ dc_options_count.times do
38
+ dc_option, bytes_read = DcOption.deserialize_from(data[offset..])
39
+ dc_options << dc_option
40
+ offset += bytes_read
41
+ end
42
+
43
+ new(
44
+ flags: flags,
45
+ date: date,
46
+ expires: expires,
47
+ test_mode: test_mode,
48
+ this_dc: this_dc,
49
+ dc_options: dc_options
50
+ )
51
+ end
52
+
53
+ def initialize(flags:, date:, expires:, test_mode:, this_dc:, dc_options:)
54
+ @flags = flags
55
+ @date = date
56
+ @expires = expires
57
+ @test_mode = test_mode
58
+ @this_dc = this_dc
59
+ @dc_options = dc_options
60
+ end
61
+ end
62
+
63
+ class DcOption
64
+ attr_reader :id, :ip_address, :port, :flags
65
+
66
+ def self.deserialize_from(data)
67
+ offset = 0
68
+ constructor = data[offset, 4].unpack1('L<')
69
+ offset += 4
70
+
71
+ raise "Expected dcOption constructor" unless constructor == 0x18b7a10d
72
+
73
+ flags = data[offset, 4].unpack1('L<')
74
+ offset += 4
75
+
76
+ id = data[offset, 4].unpack1('L<')
77
+ offset += 4
78
+
79
+ ip_length = data[offset].ord
80
+ offset += 1
81
+
82
+ ip_address = data[offset, ip_length]
83
+ offset += ip_length
84
+
85
+ padding = (4 - ((ip_length + 1) % 4)) % 4
86
+ offset += padding
87
+
88
+ port = data[offset, 4].unpack1('L<')
89
+ offset += 4
90
+
91
+ [new(id: id, ip_address: ip_address, port: port, flags: flags), offset]
92
+ end
93
+
94
+ def initialize(id:, ip_address:, port:, flags:)
95
+ @id = id
96
+ @ip_address = ip_address
97
+ @port = port
98
+ @flags = flags
99
+ end
100
+
101
+ def ipv6?
102
+ (@flags & 1) != 0
103
+ end
104
+
105
+ def media_only?
106
+ (@flags & 2) != 0
107
+ end
108
+
109
+ def tcpo_only?
110
+ (@flags & 4) != 0
111
+ end
112
+
113
+ def cdn?
114
+ (@flags & 8) != 0
115
+ end
116
+
117
+ def static?
118
+ (@flags & 16) != 0
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zlib'
4
+ require 'stringio'
5
+
6
+ module MTProto
7
+ module TL
8
+ module GzipPacked
9
+ CONSTRUCTOR = 0x3072cfa1
10
+
11
+ module_function
12
+
13
+ def unpack(data)
14
+ offset = 4
15
+
16
+ length_byte = data[offset].ord
17
+ offset += 1
18
+ puts " [GZIP] Length byte: #{length_byte} (0x#{length_byte.to_s(16)})" if $DEBUG
19
+
20
+ if length_byte == 254
21
+ length_bytes = data[offset, 3]
22
+ length = (length_bytes + "\x00").unpack1('L<')
23
+ offset += 3
24
+ puts " [GZIP] Extended length: #{length} bytes" if $DEBUG
25
+ elsif length_byte == 255
26
+ raise "Invalid TL string length: 255"
27
+ else
28
+ length = length_byte
29
+ puts " [GZIP] Short length: #{length} bytes" if $DEBUG
30
+ end
31
+
32
+ raise "Invalid length: #{length.inspect}" unless length.is_a?(Integer) && length > 0
33
+
34
+ compressed_data = data[offset, length]
35
+ raise "Not enough data: expected #{length}, got #{compressed_data&.bytesize}" if compressed_data.nil? || compressed_data.bytesize < length
36
+
37
+ Zlib::GzipReader.new(StringIO.new(compressed_data)).read
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,10 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'serializer'
4
+
3
5
  module MTProto
4
6
  module TL
5
7
  class Message
6
8
  CONSTRUCTOR_REQ_PQ_MULTI = 0xbe7e8ef1
7
9
  CONSTRUCTOR_RES_PQ = 0x05162463
10
+ CONSTRUCTOR_REQ_DH_PARAMS = 0xd712e4be
11
+ CONSTRUCTOR_SERVER_DH_PARAMS_OK = 0xd0e8075c
12
+ CONSTRUCTOR_SET_CLIENT_DH_PARAMS = 0xf5045f1f
13
+ CONSTRUCTOR_DH_GEN_OK = 0x3bcbf734
14
+ CONSTRUCTOR_DH_GEN_RETRY = 0x46dc1fb9
15
+ CONSTRUCTOR_DH_GEN_FAIL = 0xa69dae02
16
+ CONSTRUCTOR_PING = 0x7abe77ec
17
+ CONSTRUCTOR_PONG = 0x347773c5
18
+ CONSTRUCTOR_BAD_MSG_NOTIFICATION = 0xa7eff811
19
+ CONSTRUCTOR_MSG_CONTAINER = 0x73f1f8dc
8
20
 
9
21
  attr_reader :auth_key_id, :msg_id, :body
10
22
 
@@ -31,6 +43,36 @@ module MTProto
31
43
  new(auth_key_id: 0, body: body)
32
44
  end
33
45
 
46
+ def self.req_DH_params(nonce:, server_nonce:, p:, q:, public_key_fingerprint:, encrypted_data:)
47
+ raise ArgumentError, 'Nonce must be 16 bytes' unless nonce.bytesize == 16
48
+ raise ArgumentError, 'Server nonce must be 16 bytes' unless server_nonce.bytesize == 16
49
+
50
+ p_bytes = Serializer.integer_to_bytes(p)
51
+ q_bytes = Serializer.integer_to_bytes(q)
52
+
53
+ body = Serializer.serialize_int(CONSTRUCTOR_REQ_DH_PARAMS)
54
+ body += nonce
55
+ body += server_nonce
56
+ body += Serializer.serialize_bytes(p_bytes)
57
+ body += Serializer.serialize_bytes(q_bytes)
58
+ body += Serializer.serialize_long(public_key_fingerprint)
59
+ body += Serializer.serialize_bytes(encrypted_data)
60
+
61
+ new(auth_key_id: 0, body: body)
62
+ end
63
+
64
+ def self.set_client_DH_params(nonce:, server_nonce:, encrypted_data:)
65
+ raise ArgumentError, 'Nonce must be 16 bytes' unless nonce.bytesize == 16
66
+ raise ArgumentError, 'Server nonce must be 16 bytes' unless server_nonce.bytesize == 16
67
+
68
+ body = Serializer.serialize_int(CONSTRUCTOR_SET_CLIENT_DH_PARAMS)
69
+ body += nonce
70
+ body += server_nonce
71
+ body += Serializer.serialize_bytes(encrypted_data)
72
+
73
+ new(auth_key_id: 0, body: body)
74
+ end
75
+
34
76
  def self.deserialize(data)
35
77
  auth_key_id = data[0, 8].unpack1('Q<')
36
78
  msg_id = data[8, 8].unpack1('Q<')
@@ -87,11 +129,109 @@ module MTProto
87
129
  }
88
130
  end
89
131
 
132
+ def parse_server_DH_params_ok
133
+ constructor = @body[0, 4].unpack1('L<')
134
+ raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == CONSTRUCTOR_SERVER_DH_PARAMS_OK
135
+
136
+ offset = 4
137
+
138
+ nonce = @body[offset, 16]
139
+ offset += 16
140
+
141
+ server_nonce = @body[offset, 16]
142
+ offset += 16
143
+
144
+ length_byte = @body[offset].ord
145
+ offset += 1
146
+
147
+ if length_byte == 254
148
+ length_bytes = @body[offset, 3].bytes
149
+ encrypted_answer_length = length_bytes[0] | (length_bytes[1] << 8) | (length_bytes[2] << 16)
150
+ offset += 3
151
+ else
152
+ encrypted_answer_length = length_byte
153
+ end
154
+
155
+ encrypted_answer = @body[offset, encrypted_answer_length]
156
+
157
+ {
158
+ nonce: nonce,
159
+ server_nonce: server_nonce,
160
+ encrypted_answer: encrypted_answer
161
+ }
162
+ end
163
+
164
+ def parse_dh_gen_response
165
+ constructor = @body[0, 4].unpack1('L<')
166
+
167
+ offset = 4
168
+ nonce = @body[offset, 16]
169
+ offset += 16
170
+
171
+ server_nonce = @body[offset, 16]
172
+ offset += 16
173
+
174
+ new_nonce_hash = @body[offset, 16]
175
+
176
+ case constructor
177
+ when CONSTRUCTOR_DH_GEN_OK
178
+ { status: :ok, nonce: nonce, server_nonce: server_nonce, new_nonce_hash: new_nonce_hash }
179
+ when CONSTRUCTOR_DH_GEN_RETRY
180
+ { status: :retry, nonce: nonce, server_nonce: server_nonce, new_nonce_hash: new_nonce_hash }
181
+ when CONSTRUCTOR_DH_GEN_FAIL
182
+ { status: :fail, nonce: nonce, server_nonce: server_nonce, new_nonce_hash: new_nonce_hash }
183
+ else
184
+ raise "Unexpected constructor: 0x#{constructor.to_s(16)}"
185
+ end
186
+ end
187
+
188
+ def self.ping(ping_id)
189
+ body = Serializer.serialize_int(CONSTRUCTOR_PING)
190
+ body += [ping_id].pack('Q<')
191
+
192
+ body
193
+ end
194
+
195
+ def parse_pong
196
+ constructor = @body[0, 4].unpack1('L<')
197
+
198
+ if constructor == CONSTRUCTOR_BAD_MSG_NOTIFICATION
199
+ bad_msg = TL::BadMsgNotification.deserialize(@body)
200
+ raise "Bad message notification: #{bad_msg.error_message} (code: #{bad_msg.error_code}, msg_id: #{bad_msg.bad_msg_id}, seqno: #{bad_msg.bad_msg_seqno})"
201
+ end
202
+
203
+ if constructor == CONSTRUCTOR_MSG_CONTAINER
204
+ container = TL::MsgContainer.deserialize(@body)
205
+ pong_message = container.messages.find do |msg|
206
+ msg[:body][0, 4].unpack1('L<') == CONSTRUCTOR_PONG
207
+ end
208
+
209
+ raise 'No pong message found in container' unless pong_message
210
+
211
+ offset = 4
212
+ msg_id = pong_message[:body][offset, 8].unpack1('Q<')
213
+ offset += 8
214
+ ping_id = pong_message[:body][offset, 8].unpack1('Q<')
215
+
216
+ return { msg_id: msg_id, ping_id: ping_id }
217
+ end
218
+
219
+ raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == CONSTRUCTOR_PONG
220
+
221
+ offset = 4
222
+ msg_id = @body[offset, 8].unpack1('Q<')
223
+ offset += 8
224
+ ping_id = @body[offset, 8].unpack1('Q<')
225
+
226
+ { msg_id: msg_id, ping_id: ping_id }
227
+ end
228
+
90
229
  private
91
230
 
92
231
  def generate_msg_id
93
- time_ns = (Time.now.to_f * 1_000_000_000).to_i
94
- (time_ns / 4) * 4
232
+ time = Time.now.to_f
233
+ msg_id = (time * (2**32)).to_i
234
+ (msg_id / 4) * 4
95
235
  end
96
236
 
97
237
  def padding_length(length)
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class MsgContainer
6
+ CONSTRUCTOR = 0x73f1f8dc
7
+
8
+ attr_reader :messages
9
+
10
+ def self.deserialize(data)
11
+ offset = 4
12
+ message_count = data[offset, 4].unpack1('L<')
13
+ offset += 4
14
+
15
+ messages = []
16
+ message_count.times do
17
+ msg_id = data[offset, 8].unpack1('Q<')
18
+ offset += 8
19
+
20
+ seqno = data[offset, 4].unpack1('L<')
21
+ offset += 4
22
+
23
+ bytes = data[offset, 4].unpack1('L<')
24
+ offset += 4
25
+
26
+ body = data[offset, bytes]
27
+ offset += bytes
28
+
29
+ messages << { msg_id: msg_id, seqno: seqno, body: body }
30
+ end
31
+
32
+ new(messages: messages)
33
+ end
34
+
35
+ def initialize(messages:)
36
+ @messages = messages
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class NewSessionCreated
6
+ CONSTRUCTOR = 0x9ec20908
7
+
8
+ attr_reader :first_msg_id, :unique_id, :server_salt
9
+
10
+ def self.deserialize(data)
11
+ offset = 4
12
+ first_msg_id = data[offset, 8].unpack1('Q<')
13
+ offset += 8
14
+
15
+ unique_id = data[offset, 8].unpack1('Q<')
16
+ offset += 8
17
+
18
+ server_salt = data[offset, 8].unpack1('Q<')
19
+
20
+ new(first_msg_id: first_msg_id, unique_id: unique_id, server_salt: server_salt)
21
+ end
22
+
23
+ def initialize(first_msg_id:, unique_id:, server_salt:)
24
+ @first_msg_id = first_msg_id
25
+ @unique_id = unique_id
26
+ @server_salt = server_salt
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'serializer'
4
+
5
+ module MTProto
6
+ module TL
7
+ class PQInnerData
8
+ CONSTRUCTOR_DC = 0xa9f55f95
9
+ CONSTRUCTOR_TEMP_DC = 0x56fddf88
10
+
11
+ attr_reader :pq, :p, :q, :nonce, :server_nonce, :new_nonce, :dc, :expires_in
12
+
13
+ def initialize(pq:, p:, q:, nonce:, server_nonce:, new_nonce:, dc:, expires_in: nil)
14
+ @pq = pq
15
+ @p = p
16
+ @q = q
17
+ @nonce = nonce
18
+ @server_nonce = server_nonce
19
+ @new_nonce = new_nonce
20
+ @dc = dc
21
+ @expires_in = expires_in
22
+ end
23
+
24
+ def serialize
25
+ constructor = @expires_in ? CONSTRUCTOR_TEMP_DC : CONSTRUCTOR_DC
26
+
27
+ data = Serializer.serialize_int(constructor)
28
+ data += Serializer.serialize_bytes(Serializer.integer_to_bytes(@pq))
29
+ data += Serializer.serialize_bytes(Serializer.integer_to_bytes(@p))
30
+ data += Serializer.serialize_bytes(Serializer.integer_to_bytes(@q))
31
+ data += @nonce
32
+ data += @server_nonce
33
+ data += @new_nonce
34
+
35
+ data += Serializer.serialize_int(@expires_in) if @expires_in
36
+
37
+ data + Serializer.serialize_int(@dc)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class RpcError
6
+ CONSTRUCTOR = 0x2144ca19
7
+
8
+ attr_reader :error_code, :error_message
9
+
10
+ def self.deserialize(data)
11
+ offset = 4
12
+ error_code = data[offset, 4].unpack1('l<')
13
+ offset += 4
14
+
15
+ message_length = data[offset].ord
16
+ offset += 1
17
+
18
+ error_message = data[offset, message_length]
19
+ offset += message_length
20
+
21
+ new(error_code: error_code, error_message: error_message)
22
+ end
23
+
24
+ def initialize(error_code:, error_message:)
25
+ @error_code = error_code
26
+ @error_message = error_message
27
+ end
28
+
29
+ def to_s
30
+ "RPC Error #{@error_code}: #{@error_message}"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ module Serializer
6
+ module_function
7
+
8
+ def serialize_int(value)
9
+ [value].pack('L<')
10
+ end
11
+
12
+ def serialize_long(value)
13
+ [value].pack('Q<')
14
+ end
15
+
16
+ def serialize_int128(value)
17
+ value.is_a?(String) ? value : [value].pack('Q<Q<')
18
+ end
19
+
20
+ def serialize_int256(value)
21
+ value.is_a?(String) ? value : [value].pack('Q<Q<Q<Q<')
22
+ end
23
+
24
+ def serialize_bytes(bytes)
25
+ length = bytes.bytesize
26
+
27
+ if length <= 253
28
+ [length].pack('C').b + bytes.b + padding(length + 1)
29
+ else
30
+ [254].pack('C').b + [length].pack('L<')[0, 3].b + bytes.b + padding(length + 4)
31
+ end
32
+ end
33
+
34
+ def serialize_string(str)
35
+ serialize_bytes(str)
36
+ end
37
+
38
+ def padding(current_length)
39
+ pad_length = (4 - (current_length % 4)) % 4
40
+ ("\x00" * pad_length).b
41
+ end
42
+
43
+ def integer_to_bytes(int)
44
+ return "\x00" if int.zero?
45
+
46
+ bytes = []
47
+ while int > 0
48
+ bytes.unshift(int & 0xff)
49
+ int >>= 8
50
+ end
51
+ bytes.pack('C*')
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MTProto
4
+ module TL
5
+ class ServerDHInnerData
6
+ CONSTRUCTOR = 0xb5890dba
7
+
8
+ attr_reader :nonce, :server_nonce, :g, :dh_prime, :g_a, :server_time
9
+
10
+ def self.deserialize(data)
11
+ constructor = data[0, 4].unpack1('L<')
12
+ raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == CONSTRUCTOR
13
+
14
+ offset = 4
15
+
16
+ nonce = data[offset, 16]
17
+ offset += 16
18
+
19
+ server_nonce = data[offset, 16]
20
+ offset += 16
21
+
22
+ g = data[offset, 4].unpack1('L<')
23
+ offset += 4
24
+
25
+ dh_prime_length_byte = data[offset].ord
26
+ offset += 1
27
+
28
+ if dh_prime_length_byte == 254
29
+ length_bytes = data[offset, 3].bytes
30
+ dh_prime_length = length_bytes[0] | (length_bytes[1] << 8) | (length_bytes[2] << 16)
31
+ offset += 3
32
+ else
33
+ dh_prime_length = dh_prime_length_byte
34
+ end
35
+
36
+ dh_prime = data[offset, dh_prime_length]
37
+ offset += dh_prime_length
38
+ offset += padding_length(dh_prime_length_byte == 254 ? dh_prime_length + 4 : dh_prime_length + 1)
39
+
40
+ g_a_length_byte = data[offset].ord
41
+ offset += 1
42
+
43
+ if g_a_length_byte == 254
44
+ length_bytes = data[offset, 3].bytes
45
+ g_a_length = length_bytes[0] | (length_bytes[1] << 8) | (length_bytes[2] << 16)
46
+ offset += 3
47
+ else
48
+ g_a_length = g_a_length_byte
49
+ end
50
+
51
+ g_a = data[offset, g_a_length]
52
+ offset += g_a_length
53
+ offset += padding_length(g_a_length_byte == 254 ? g_a_length + 4 : g_a_length + 1)
54
+
55
+ server_time = data[offset, 4].unpack1('L<')
56
+
57
+ new(
58
+ nonce: nonce,
59
+ server_nonce: server_nonce,
60
+ g: g,
61
+ dh_prime: dh_prime,
62
+ g_a: g_a,
63
+ server_time: server_time
64
+ )
65
+ end
66
+
67
+ def initialize(nonce:, server_nonce:, g:, dh_prime:, g_a:, server_time:)
68
+ @nonce = nonce
69
+ @server_nonce = server_nonce
70
+ @g = g
71
+ @dh_prime = dh_prime
72
+ @g_a = g_a
73
+ @server_time = server_time
74
+ end
75
+
76
+ def self.padding_length(length)
77
+ (4 - (length % 4)) % 4
78
+ end
79
+
80
+ def padding_length(length)
81
+ self.class.padding_length(length)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MTProto
4
- VERSION = '0.0.4'
4
+ VERSION = '0.0.5'
5
5
  end
data/lib/mtproto.rb CHANGED
@@ -4,7 +4,27 @@ require_relative 'mtproto/version'
4
4
  require_relative 'mtproto/transport/abridged_packet_codec'
5
5
  require_relative 'mtproto/transport/tcp_connection'
6
6
  require_relative 'mtproto/tl/message'
7
+ require_relative 'mtproto/tl/serializer'
8
+ require_relative 'mtproto/tl/p_q_inner_data'
9
+ require_relative 'mtproto/tl/server_dh_inner_data'
10
+ require_relative 'mtproto/tl/client_dh_inner_data'
11
+ require_relative 'mtproto/tl/bad_msg_notification'
12
+ require_relative 'mtproto/tl/msg_container'
13
+ require_relative 'mtproto/tl/new_session_created'
14
+ require_relative 'mtproto/tl/rpc_error'
15
+ require_relative 'mtproto/tl/gzip_packed'
16
+ require_relative 'mtproto/tl/config'
7
17
  require_relative 'mtproto/crypto/rsa_key'
18
+ require_relative 'mtproto/crypto/factorization'
19
+ require_relative 'mtproto/crypto/aes_ige'
20
+ require_relative 'mtproto/crypto/rsa_pad'
21
+ require_relative 'mtproto/crypto/auth_key_helper'
22
+ require_relative 'mtproto/crypto/dh_validator'
23
+ require_relative 'mtproto/crypto/dh_key_exchange'
24
+ require_relative 'mtproto/crypto/message_key'
25
+ require_relative 'mtproto/auth_key_generator'
26
+ require_relative 'mtproto/session'
27
+ require_relative 'mtproto/encrypted_message'
8
28
  require_relative 'mtproto/client'
9
29
 
10
30
  module MTProto
data/tmp/.keep ADDED
File without changes