mtproto 0.0.7 → 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/.env.example +4 -0
- data/ext/aes_ige/extconf.rb +3 -9
- data/ext/factorization/extconf.rb +2 -0
- data/lib/mtproto/auth_key_generator.rb +66 -103
- 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 +67 -107
- data/lib/mtproto/client.rb +176 -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/{tl → type}/bad_msg_notification.rb +11 -11
- data/lib/mtproto/{tl → type}/client_dh_inner_data.rb +1 -1
- data/lib/mtproto/{tl → type}/gzip_packed.rb +6 -4
- data/lib/mtproto/{tl → type}/message.rb +3 -3
- data/lib/mtproto/{tl → type}/msg_container.rb +1 -1
- data/lib/mtproto/{tl → type}/new_session_created.rb +1 -1
- data/lib/mtproto/{tl/p_q_inner_data.rb → type/pq_inner_data.rb} +1 -1
- data/lib/mtproto/{tl → type}/rpc_error.rb +1 -2
- data/lib/mtproto/{tl → type}/serializer.rb +1 -1
- data/lib/mtproto/{tl → type}/server_dh_inner_data.rb +1 -1
- data/lib/mtproto/updates_poller.rb +37 -33
- data/lib/mtproto/version.rb +1 -1
- data/lib/mtproto.rb +21 -22
- data/scripts/generate_constructors.rb +65 -0
- metadata +80 -49
- data/lib/mtproto/rpc/get_config.rb +0 -37
- data/lib/mtproto/rpc/get_contacts.rb +0 -22
- data/lib/mtproto/rpc/get_updates_difference.rb +0 -33
- data/lib/mtproto/rpc/get_updates_state.rb +0 -22
- data/lib/mtproto/rpc/get_users.rb +0 -22
- data/lib/mtproto/rpc/ping.rb +0 -26
- data/lib/mtproto/rpc/send_code.rb +0 -44
- data/lib/mtproto/rpc/send_message.rb +0 -31
- data/lib/mtproto/rpc/sign_in.rb +0 -52
- data/lib/mtproto/tl/auth_key/dh_gen_response.rb +0 -37
- data/lib/mtproto/tl/auth_key/req_dh_params.rb +0 -31
- data/lib/mtproto/tl/auth_key/req_pq_multi.rb +0 -18
- data/lib/mtproto/tl/auth_key/res_pq.rb +0 -62
- data/lib/mtproto/tl/auth_key/server_dh_params.rb +0 -43
- data/lib/mtproto/tl/auth_key/set_client_dh_params.rb +0 -25
- data/lib/mtproto/tl/code_settings.rb +0 -25
- data/lib/mtproto/tl/config.rb +0 -124
- data/lib/mtproto/tl/method_builder.rb +0 -29
- data/lib/mtproto/tl/rpc/auth/authorization.rb +0 -107
- data/lib/mtproto/tl/rpc/auth/send_code.rb +0 -28
- data/lib/mtproto/tl/rpc/auth/sent_code.rb +0 -36
- data/lib/mtproto/tl/rpc/auth/sign_in.rb +0 -32
- data/lib/mtproto/tl/rpc/contacts/contacts.rb +0 -155
- data/lib/mtproto/tl/rpc/contacts/get_contacts.rb +0 -18
- data/lib/mtproto/tl/rpc/help/config.rb +0 -35
- data/lib/mtproto/tl/rpc/help/get_config.rb +0 -17
- data/lib/mtproto/tl/rpc/messages/send_message.rb +0 -43
- data/lib/mtproto/tl/rpc/messages/updates.rb +0 -87
- data/lib/mtproto/tl/rpc/ping.rb +0 -18
- data/lib/mtproto/tl/rpc/pong.rb +0 -46
- data/lib/mtproto/tl/rpc/updates/difference.rb +0 -332
- data/lib/mtproto/tl/rpc/updates/get_difference.rb +0 -42
- data/lib/mtproto/tl/rpc/updates/get_state.rb +0 -17
- data/lib/mtproto/tl/rpc/updates/state.rb +0 -59
- data/lib/mtproto/tl/rpc/users/get_users.rb +0 -25
- data/lib/mtproto/tl/rpc/users/users.rb +0 -99
- data/lib/mtproto/tl/sent_code.rb +0 -128
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'authorization'
|
|
4
|
+
|
|
5
|
+
module MTProto
|
|
6
|
+
module TL
|
|
7
|
+
class LoginToken
|
|
8
|
+
CONSTRUCTOR_TOKEN = 0x629f1980
|
|
9
|
+
CONSTRUCTOR_MIGRATE_TO = 0x068e9916
|
|
10
|
+
CONSTRUCTOR_SUCCESS = 0x390d5c5e
|
|
11
|
+
|
|
12
|
+
attr_reader :type, :expires, :token, :dc_id, :authorization
|
|
13
|
+
|
|
14
|
+
def initialize(type:, expires: nil, token: nil, dc_id: nil, authorization: nil)
|
|
15
|
+
@type = type
|
|
16
|
+
@expires = expires
|
|
17
|
+
@token = token
|
|
18
|
+
@dc_id = dc_id
|
|
19
|
+
@authorization = authorization
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def user_id
|
|
23
|
+
@authorization&.user_id
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def access_hash
|
|
27
|
+
@authorization&.access_hash
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def success?
|
|
31
|
+
@type == :success
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.parse(data)
|
|
35
|
+
constructor = data[0, 4].unpack1('L<')
|
|
36
|
+
|
|
37
|
+
case constructor
|
|
38
|
+
when CONSTRUCTOR_TOKEN
|
|
39
|
+
parse_token(data)
|
|
40
|
+
when CONSTRUCTOR_MIGRATE_TO
|
|
41
|
+
parse_migrate_to(data)
|
|
42
|
+
when CONSTRUCTOR_SUCCESS
|
|
43
|
+
new(type: :success, authorization: Authorization.parse(data[4..]))
|
|
44
|
+
else
|
|
45
|
+
raise UnexpectedConstructorError, constructor
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class << self
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def parse_token(data)
|
|
53
|
+
offset = 4
|
|
54
|
+
expires = data[offset, 4].unpack1('L<')
|
|
55
|
+
offset += 4
|
|
56
|
+
token = read_tl_bytes(data, offset)
|
|
57
|
+
new(type: :token, expires: expires, token: token)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def parse_migrate_to(data)
|
|
61
|
+
offset = 4
|
|
62
|
+
dc_id = data[offset, 4].unpack1('L<')
|
|
63
|
+
offset += 4
|
|
64
|
+
token = read_tl_bytes(data, offset)
|
|
65
|
+
new(type: :migrate_to, dc_id: dc_id, token: token)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def read_tl_bytes(data, offset)
|
|
69
|
+
first_byte = data.getbyte(offset)
|
|
70
|
+
if first_byte < 254
|
|
71
|
+
data[offset + 1, first_byte]
|
|
72
|
+
else
|
|
73
|
+
len = data.getbyte(offset + 1) |
|
|
74
|
+
(data.getbyte(offset + 2) << 8) |
|
|
75
|
+
(data.getbyte(offset + 3) << 16)
|
|
76
|
+
data[offset + 4, len]
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class PQInnerData
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
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 body
|
|
25
|
+
constructor = @expires_in ? CONSTRUCTOR_TEMP_DC : CONSTRUCTOR_DC
|
|
26
|
+
|
|
27
|
+
result = u32_b(constructor)
|
|
28
|
+
result += serialize_tl_bytes(integer_to_bytes(@pq))
|
|
29
|
+
result += serialize_tl_bytes(integer_to_bytes(@p))
|
|
30
|
+
result += serialize_tl_bytes(integer_to_bytes(@q))
|
|
31
|
+
result += @nonce.bytes
|
|
32
|
+
result += @server_nonce.bytes
|
|
33
|
+
result += @new_nonce.bytes
|
|
34
|
+
result += u32_b(@expires_in) if @expires_in
|
|
35
|
+
result += u32_b(@dc)
|
|
36
|
+
|
|
37
|
+
result
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def serialize_tl_bytes(bytes)
|
|
43
|
+
length = bytes.length
|
|
44
|
+
|
|
45
|
+
if length <= 253
|
|
46
|
+
[length] + bytes + padding(length + 1)
|
|
47
|
+
else
|
|
48
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def padding(current_length)
|
|
53
|
+
pad_length = (4 - (current_length % 4)) % 4
|
|
54
|
+
[0] * pad_length
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def integer_to_bytes(int)
|
|
58
|
+
return [0] if int.zero?
|
|
59
|
+
|
|
60
|
+
bytes = []
|
|
61
|
+
while int > 0
|
|
62
|
+
bytes.unshift(int & 0xff)
|
|
63
|
+
int >>= 8
|
|
64
|
+
end
|
|
65
|
+
bytes
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ReqDHParams
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0xd712e4be
|
|
9
|
+
|
|
10
|
+
attr_reader :nonce, :server_nonce, :p, :q, :public_key_fingerprint, :encrypted_data
|
|
11
|
+
|
|
12
|
+
def initialize(nonce:, server_nonce:, p:, q:, public_key_fingerprint:, encrypted_data:)
|
|
13
|
+
raise ArgumentError, 'Nonce must be 16 bytes' unless nonce.bytesize == 16
|
|
14
|
+
raise ArgumentError, 'Server nonce must be 16 bytes' unless server_nonce.bytesize == 16
|
|
15
|
+
|
|
16
|
+
@nonce = nonce
|
|
17
|
+
@server_nonce = server_nonce
|
|
18
|
+
@p = p
|
|
19
|
+
@q = q
|
|
20
|
+
@public_key_fingerprint = public_key_fingerprint
|
|
21
|
+
@encrypted_data = encrypted_data
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def body
|
|
25
|
+
result = u32_b(CONSTRUCTOR)
|
|
26
|
+
result += @nonce.bytes
|
|
27
|
+
result += @server_nonce.bytes
|
|
28
|
+
result += serialize_tl_bytes(integer_to_bytes(@p))
|
|
29
|
+
result += serialize_tl_bytes(integer_to_bytes(@q))
|
|
30
|
+
result += u64_b(@public_key_fingerprint)
|
|
31
|
+
result += serialize_tl_bytes(@encrypted_data.bytes)
|
|
32
|
+
|
|
33
|
+
result
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def serialize_tl_bytes(bytes)
|
|
39
|
+
length = bytes.length
|
|
40
|
+
|
|
41
|
+
if length <= 253
|
|
42
|
+
[length] + bytes + padding(length + 1)
|
|
43
|
+
else
|
|
44
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def padding(current_length)
|
|
49
|
+
pad_length = (4 - (current_length % 4)) % 4
|
|
50
|
+
[0] * pad_length
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def integer_to_bytes(int)
|
|
54
|
+
return [0] if int.zero?
|
|
55
|
+
|
|
56
|
+
bytes = []
|
|
57
|
+
while int > 0
|
|
58
|
+
bytes.unshift(int & 0xff)
|
|
59
|
+
int >>= 8
|
|
60
|
+
end
|
|
61
|
+
bytes
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ReqPqMulti
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0xbe7e8ef1
|
|
9
|
+
|
|
10
|
+
attr_reader :nonce
|
|
11
|
+
|
|
12
|
+
def initialize(nonce)
|
|
13
|
+
raise ArgumentError, 'Nonce must be 16 bytes' unless nonce.bytesize == 16
|
|
14
|
+
|
|
15
|
+
@nonce = nonce
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def body
|
|
19
|
+
u32_b(CONSTRUCTOR) + @nonce.bytes
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ResPq
|
|
6
|
+
extend Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0x05162463
|
|
9
|
+
|
|
10
|
+
attr_reader :nonce, :server_nonce, :pq, :fingerprints
|
|
11
|
+
|
|
12
|
+
def self.parse(message)
|
|
13
|
+
data = message.body
|
|
14
|
+
constructor = b_u32(data[0, 4])
|
|
15
|
+
raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == CONSTRUCTOR
|
|
16
|
+
|
|
17
|
+
offset = 4
|
|
18
|
+
|
|
19
|
+
nonce = data[offset, 16].pack('C*')
|
|
20
|
+
offset += 16
|
|
21
|
+
|
|
22
|
+
server_nonce = data[offset, 16].pack('C*')
|
|
23
|
+
offset += 16
|
|
24
|
+
|
|
25
|
+
pq_length_byte = data[offset]
|
|
26
|
+
offset += 1
|
|
27
|
+
|
|
28
|
+
pq_length = if pq_length_byte == 254
|
|
29
|
+
b_u32(data[offset, 3] + [0]) & 0xffffff
|
|
30
|
+
offset += 3
|
|
31
|
+
else
|
|
32
|
+
pq_length_byte
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
pq = data[offset, pq_length].pack('C*')
|
|
36
|
+
offset += pq_length
|
|
37
|
+
offset += padding_length(pq_length + 1)
|
|
38
|
+
|
|
39
|
+
vector_constructor = b_u32(data[offset, 4])
|
|
40
|
+
offset += 4
|
|
41
|
+
raise 'Expected vector constructor' unless vector_constructor == 0x1cb5c415
|
|
42
|
+
|
|
43
|
+
fingerprints_count = b_u32(data[offset, 4])
|
|
44
|
+
offset += 4
|
|
45
|
+
|
|
46
|
+
fingerprints = []
|
|
47
|
+
fingerprints_count.times do
|
|
48
|
+
fingerprints << b_u64(data[offset, 8])
|
|
49
|
+
offset += 8
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
new(
|
|
53
|
+
nonce: nonce,
|
|
54
|
+
server_nonce: server_nonce,
|
|
55
|
+
pq: pq,
|
|
56
|
+
fingerprints: fingerprints
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.padding_length(length)
|
|
61
|
+
(4 - (length % 4)) % 4
|
|
62
|
+
end
|
|
63
|
+
private_class_method :padding_length
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def initialize(nonce:, server_nonce:, pq:, fingerprints:)
|
|
68
|
+
@nonce = nonce
|
|
69
|
+
@server_nonce = server_nonce
|
|
70
|
+
@pq = pq
|
|
71
|
+
@fingerprints = fingerprints
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class SendCode
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0xa677244f
|
|
9
|
+
CODE_SETTINGS = 0xad253d78
|
|
10
|
+
|
|
11
|
+
attr_reader :phone_number, :api_id, :api_hash
|
|
12
|
+
|
|
13
|
+
def initialize(phone_number:, api_id:, api_hash:)
|
|
14
|
+
@phone_number = phone_number
|
|
15
|
+
@api_id = api_id
|
|
16
|
+
@api_hash = api_hash
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def body
|
|
20
|
+
u32_b(CONSTRUCTOR) +
|
|
21
|
+
serialize_tl_string(@phone_number) +
|
|
22
|
+
u32_b(@api_id) +
|
|
23
|
+
serialize_tl_string(@api_hash) +
|
|
24
|
+
code_settings
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def code_settings
|
|
30
|
+
u32_b(CODE_SETTINGS) + u32_b(0)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def serialize_tl_string(str)
|
|
34
|
+
bytes = str.encode('UTF-8').bytes
|
|
35
|
+
length = bytes.length
|
|
36
|
+
|
|
37
|
+
if length <= 253
|
|
38
|
+
[length] + bytes + padding(length + 1)
|
|
39
|
+
else
|
|
40
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def padding(current_length)
|
|
45
|
+
pad_length = (4 - (current_length % 4)) % 4
|
|
46
|
+
[0] * pad_length
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class SentCode
|
|
6
|
+
CONSTRUCTOR = 0x5e002502
|
|
7
|
+
|
|
8
|
+
attr_reader :flags, :type, :phone_code_hash, :next_type, :timeout
|
|
9
|
+
|
|
10
|
+
def initialize(flags:, type:, phone_code_hash:, next_type: nil, timeout: nil)
|
|
11
|
+
@flags = flags
|
|
12
|
+
@type = type
|
|
13
|
+
@phone_code_hash = phone_code_hash
|
|
14
|
+
@next_type = next_type
|
|
15
|
+
@timeout = timeout
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.parse(data)
|
|
19
|
+
constructor = data[0, 4].unpack1('L<')
|
|
20
|
+
raise UnexpectedConstructorError, constructor unless constructor == CONSTRUCTOR
|
|
21
|
+
|
|
22
|
+
offset = 4
|
|
23
|
+
flags = data[offset, 4].unpack1('L<')
|
|
24
|
+
offset += 4
|
|
25
|
+
|
|
26
|
+
type, offset = parse_sent_code_type(data, offset)
|
|
27
|
+
|
|
28
|
+
phone_code_hash, offset = read_tl_string(data, offset)
|
|
29
|
+
|
|
30
|
+
next_type = nil
|
|
31
|
+
next_type, offset = parse_code_type(data, offset) if flags.anybits?(1 << 1)
|
|
32
|
+
|
|
33
|
+
timeout = nil
|
|
34
|
+
timeout = data[offset, 4].unpack1('L<') if flags.anybits?(1 << 2)
|
|
35
|
+
|
|
36
|
+
new(flags: flags, type: type, phone_code_hash: phone_code_hash, next_type: next_type, timeout: timeout)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class << self
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def parse_sent_code_type(data, offset)
|
|
43
|
+
constructor = data[offset, 4].unpack1('L<')
|
|
44
|
+
offset += 4
|
|
45
|
+
|
|
46
|
+
case constructor
|
|
47
|
+
when 0x3dbb5986, 0xc000bba2, 0x5353e5a7 # app, sms, call
|
|
48
|
+
length = data[offset, 4].unpack1('L<')
|
|
49
|
+
offset += 4
|
|
50
|
+
[{ length: length }, offset]
|
|
51
|
+
else
|
|
52
|
+
[{}, offset]
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def parse_code_type(data, offset)
|
|
57
|
+
constructor = data[offset, 4].unpack1('L<')
|
|
58
|
+
offset += 4
|
|
59
|
+
[constructor, offset]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def read_tl_string(data, offset)
|
|
63
|
+
first_byte = data.getbyte(offset)
|
|
64
|
+
if first_byte == 254
|
|
65
|
+
len = data.getbyte(offset + 1) |
|
|
66
|
+
(data.getbyte(offset + 2) << 8) |
|
|
67
|
+
(data.getbyte(offset + 3) << 16)
|
|
68
|
+
total = 4 + len
|
|
69
|
+
else
|
|
70
|
+
len = first_byte
|
|
71
|
+
total = 1 + len
|
|
72
|
+
end
|
|
73
|
+
padding = (4 - (total % 4)) % 4
|
|
74
|
+
[data[offset + (total - len), len], offset + total + padding]
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
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.parse(bytes)
|
|
11
|
+
constructor = bytes[0, 4].unpack1('L<')
|
|
12
|
+
raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == CONSTRUCTOR
|
|
13
|
+
|
|
14
|
+
offset = 4
|
|
15
|
+
|
|
16
|
+
nonce = bytes[offset, 16]
|
|
17
|
+
offset += 16
|
|
18
|
+
|
|
19
|
+
server_nonce = bytes[offset, 16]
|
|
20
|
+
offset += 16
|
|
21
|
+
|
|
22
|
+
g = bytes[offset, 4].unpack1('L<')
|
|
23
|
+
offset += 4
|
|
24
|
+
|
|
25
|
+
dh_prime, offset = read_tl_bytes(bytes, offset)
|
|
26
|
+
g_a, offset = read_tl_bytes(bytes, offset)
|
|
27
|
+
|
|
28
|
+
server_time = bytes[offset, 4].unpack1('L<')
|
|
29
|
+
|
|
30
|
+
new(
|
|
31
|
+
nonce: nonce,
|
|
32
|
+
server_nonce: server_nonce,
|
|
33
|
+
g: g,
|
|
34
|
+
dh_prime: dh_prime,
|
|
35
|
+
g_a: g_a,
|
|
36
|
+
server_time: server_time
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.read_tl_bytes(bytes, offset)
|
|
41
|
+
length_byte = bytes[offset].ord
|
|
42
|
+
offset += 1
|
|
43
|
+
|
|
44
|
+
if length_byte == 254
|
|
45
|
+
length_bytes = bytes[offset, 3].bytes
|
|
46
|
+
length = length_bytes[0] | (length_bytes[1] << 8) | (length_bytes[2] << 16)
|
|
47
|
+
offset += 3
|
|
48
|
+
header_size = 4
|
|
49
|
+
else
|
|
50
|
+
length = length_byte
|
|
51
|
+
header_size = 1
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
value = bytes[offset, length]
|
|
55
|
+
offset += length
|
|
56
|
+
offset += (4 - ((header_size + length) % 4)) % 4
|
|
57
|
+
|
|
58
|
+
[value, offset]
|
|
59
|
+
end
|
|
60
|
+
private_class_method :read_tl_bytes
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def initialize(nonce:, server_nonce:, g:, dh_prime:, g_a:, server_time:)
|
|
65
|
+
@nonce = nonce
|
|
66
|
+
@server_nonce = server_nonce
|
|
67
|
+
@g = g
|
|
68
|
+
@dh_prime = dh_prime
|
|
69
|
+
@g_a = g_a
|
|
70
|
+
@server_time = server_time
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class ServerDHParams
|
|
6
|
+
extend Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0xd0e8075c
|
|
9
|
+
|
|
10
|
+
attr_reader :nonce, :server_nonce, :encrypted_answer
|
|
11
|
+
|
|
12
|
+
def self.parse(message)
|
|
13
|
+
data = message.body
|
|
14
|
+
constructor = b_u32(data[0, 4])
|
|
15
|
+
raise "Unexpected constructor: 0x#{constructor.to_s(16)}" unless constructor == CONSTRUCTOR
|
|
16
|
+
|
|
17
|
+
offset = 4
|
|
18
|
+
|
|
19
|
+
nonce = data[offset, 16].pack('C*')
|
|
20
|
+
offset += 16
|
|
21
|
+
|
|
22
|
+
server_nonce = data[offset, 16].pack('C*')
|
|
23
|
+
offset += 16
|
|
24
|
+
|
|
25
|
+
length_byte = data[offset]
|
|
26
|
+
offset += 1
|
|
27
|
+
|
|
28
|
+
if length_byte == 254
|
|
29
|
+
encrypted_answer_length = data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16)
|
|
30
|
+
offset += 3
|
|
31
|
+
else
|
|
32
|
+
encrypted_answer_length = length_byte
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
encrypted_answer = data[offset, encrypted_answer_length].pack('C*')
|
|
36
|
+
|
|
37
|
+
new(
|
|
38
|
+
nonce: nonce,
|
|
39
|
+
server_nonce: server_nonce,
|
|
40
|
+
encrypted_answer: encrypted_answer
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def initialize(nonce:, server_nonce:, encrypted_answer:)
|
|
47
|
+
@nonce = nonce
|
|
48
|
+
@server_nonce = server_nonce
|
|
49
|
+
@encrypted_answer = encrypted_answer
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class SetClientDHParams
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0xf5045f1f
|
|
9
|
+
|
|
10
|
+
attr_reader :nonce, :server_nonce, :encrypted_data
|
|
11
|
+
|
|
12
|
+
def initialize(nonce:, server_nonce:, encrypted_data:)
|
|
13
|
+
raise ArgumentError, 'Nonce must be 16 bytes' unless nonce.bytesize == 16
|
|
14
|
+
raise ArgumentError, 'Server nonce must be 16 bytes' unless server_nonce.bytesize == 16
|
|
15
|
+
|
|
16
|
+
@nonce = nonce
|
|
17
|
+
@server_nonce = server_nonce
|
|
18
|
+
@encrypted_data = encrypted_data
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def body
|
|
22
|
+
result = u32_b(CONSTRUCTOR)
|
|
23
|
+
result += @nonce.bytes
|
|
24
|
+
result += @server_nonce.bytes
|
|
25
|
+
result += serialize_tl_bytes(@encrypted_data.bytes)
|
|
26
|
+
|
|
27
|
+
result
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def serialize_tl_bytes(bytes)
|
|
33
|
+
length = bytes.length
|
|
34
|
+
|
|
35
|
+
if length <= 253
|
|
36
|
+
[length] + bytes + padding(length + 1)
|
|
37
|
+
else
|
|
38
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def padding(current_length)
|
|
43
|
+
pad_length = (4 - (current_length % 4)) % 4
|
|
44
|
+
[0] * pad_length
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MTProto
|
|
4
|
+
module TL
|
|
5
|
+
class SignIn
|
|
6
|
+
include Binary
|
|
7
|
+
|
|
8
|
+
CONSTRUCTOR = 0x8d52a951
|
|
9
|
+
|
|
10
|
+
attr_reader :phone_number, :phone_code_hash, :phone_code
|
|
11
|
+
|
|
12
|
+
def initialize(phone_number:, phone_code_hash:, phone_code:)
|
|
13
|
+
@phone_number = phone_number
|
|
14
|
+
@phone_code_hash = phone_code_hash
|
|
15
|
+
@phone_code = phone_code
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def body
|
|
19
|
+
flags = 1 << 0
|
|
20
|
+
|
|
21
|
+
u32_b(CONSTRUCTOR) +
|
|
22
|
+
u32_b(flags) +
|
|
23
|
+
serialize_tl_string(@phone_number) +
|
|
24
|
+
serialize_tl_string(@phone_code_hash) +
|
|
25
|
+
serialize_tl_string(@phone_code)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def serialize_tl_string(str)
|
|
31
|
+
bytes = str.encode('UTF-8').bytes
|
|
32
|
+
length = bytes.length
|
|
33
|
+
|
|
34
|
+
if length <= 253
|
|
35
|
+
[length] + bytes + padding(length + 1)
|
|
36
|
+
else
|
|
37
|
+
[254] + u32_b(length)[0, 3] + bytes + padding(length + 4)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def padding(current_length)
|
|
42
|
+
pad_length = (4 - (current_length % 4)) % 4
|
|
43
|
+
[0] * pad_length
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|