mtproto 0.0.4 → 0.0.6

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +5 -0
  3. data/Rakefile +26 -1
  4. data/ext/aes_ige/Makefile +273 -0
  5. data/ext/aes_ige/aes_ige.c +103 -0
  6. data/ext/aes_ige/extconf.rb +25 -0
  7. data/ext/factorization/Makefile +273 -0
  8. data/ext/factorization/extconf.rb +3 -0
  9. data/ext/factorization/factorization.c +62 -0
  10. data/lib/mtproto/auth_key_generator.rb +241 -0
  11. data/lib/mtproto/client.rb +217 -20
  12. data/lib/mtproto/connection.rb +103 -0
  13. data/lib/mtproto/crypto/aes_ige.rb +23 -0
  14. data/lib/mtproto/crypto/auth_key_helper.rb +25 -0
  15. data/lib/mtproto/crypto/dh_key_exchange.rb +44 -0
  16. data/lib/mtproto/crypto/dh_validator.rb +80 -0
  17. data/lib/mtproto/crypto/factorization.rb +39 -0
  18. data/lib/mtproto/crypto/message_key.rb +32 -0
  19. data/lib/mtproto/crypto/rsa_key.rb +9 -15
  20. data/lib/mtproto/crypto/rsa_pad.rb +59 -0
  21. data/lib/mtproto/encrypted_message.rb +86 -0
  22. data/lib/mtproto/errors.rb +33 -0
  23. data/lib/mtproto/session.rb +20 -0
  24. data/lib/mtproto/tl/bad_msg_notification.rb +46 -0
  25. data/lib/mtproto/tl/client_dh_inner_data.rb +29 -0
  26. data/lib/mtproto/tl/code_settings.rb +25 -0
  27. data/lib/mtproto/tl/config.rb +124 -0
  28. data/lib/mtproto/tl/gzip_packed.rb +41 -0
  29. data/lib/mtproto/tl/message.rb +148 -2
  30. data/lib/mtproto/tl/msg_container.rb +40 -0
  31. data/lib/mtproto/tl/new_session_created.rb +30 -0
  32. data/lib/mtproto/tl/p_q_inner_data.rb +41 -0
  33. data/lib/mtproto/tl/rpc_error.rb +34 -0
  34. data/lib/mtproto/tl/sent_code.rb +128 -0
  35. data/lib/mtproto/tl/serializer.rb +55 -0
  36. data/lib/mtproto/tl/server_dh_inner_data.rb +85 -0
  37. data/lib/mtproto/transport/tcp_connection.rb +1 -1
  38. data/lib/mtproto/version.rb +1 -1
  39. data/lib/mtproto.rb +24 -0
  40. data/tmp/.keep +0 -0
  41. metadata +33 -1
@@ -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,128 @@
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 self.deserialize(data)
11
+ offset = 4
12
+
13
+ flags = data[offset, 4].unpack1('L<')
14
+ offset += 4
15
+
16
+ type, type_bytes_read = SentCodeType.deserialize_from(data[offset..])
17
+ offset += type_bytes_read
18
+
19
+ phone_code_hash_length = data[offset].ord
20
+ offset += 1
21
+
22
+ phone_code_hash = data[offset, phone_code_hash_length]
23
+ offset += phone_code_hash_length
24
+
25
+ padding = (4 - ((phone_code_hash_length + 1) % 4)) % 4
26
+ offset += padding
27
+
28
+ next_type = nil
29
+ if (flags & (1 << 1)) != 0
30
+ next_type, next_type_bytes_read = CodeType.deserialize_from(data[offset..])
31
+ offset += next_type_bytes_read
32
+ end
33
+
34
+ timeout = nil
35
+ if (flags & (1 << 2)) != 0
36
+ timeout = data[offset, 4].unpack1('L<')
37
+ end
38
+
39
+ new(
40
+ flags: flags,
41
+ type: type,
42
+ phone_code_hash: phone_code_hash,
43
+ next_type: next_type,
44
+ timeout: timeout
45
+ )
46
+ end
47
+
48
+ def initialize(flags:, type:, phone_code_hash:, next_type: nil, timeout: nil)
49
+ @flags = flags
50
+ @type = type
51
+ @phone_code_hash = phone_code_hash
52
+ @next_type = next_type
53
+ @timeout = timeout
54
+ end
55
+
56
+ def to_h
57
+ {
58
+ phone_code_hash: @phone_code_hash,
59
+ type: @type,
60
+ next_type: @next_type,
61
+ timeout: @timeout
62
+ }.compact
63
+ end
64
+ end
65
+
66
+ module SentCodeType
67
+ def self.deserialize_from(data)
68
+ constructor = data[0, 4].unpack1('L<')
69
+ offset = 4
70
+
71
+ case constructor
72
+ when 0x3dbb5986 # auth.sentCodeTypeApp
73
+ length = data[offset, 4].unpack1('L<')
74
+ offset += 4
75
+ [{ _: :sent_code_type_app, length: length }, offset]
76
+ when 0xc000bba2 # auth.sentCodeTypeSms
77
+ length = data[offset, 4].unpack1('L<')
78
+ offset += 4
79
+ [{ _: :sent_code_type_sms, length: length }, offset]
80
+ when 0x5353e5a7 # auth.sentCodeTypeCall
81
+ length = data[offset, 4].unpack1('L<')
82
+ offset += 4
83
+ [{ _: :sent_code_type_call, length: length }, offset]
84
+ when 0xab03c6d9 # auth.sentCodeTypeFlashCall
85
+ pattern_length = data[offset].ord
86
+ offset += 1
87
+ pattern = data[offset, pattern_length]
88
+ offset += pattern_length
89
+ padding = (4 - ((pattern_length + 1) % 4)) % 4
90
+ offset += padding
91
+ [{ _: :sent_code_type_flash_call, pattern: pattern }, offset]
92
+ when 0x82006484 # auth.sentCodeTypeMissedCall
93
+ prefix_length = data[offset].ord
94
+ offset += 1
95
+ prefix = data[offset, prefix_length]
96
+ offset += prefix_length
97
+ padding = (4 - ((prefix_length + 1) % 4)) % 4
98
+ offset += padding
99
+ length = data[offset, 4].unpack1('L<')
100
+ offset += 4
101
+ [{ _: :sent_code_type_missed_call, prefix: prefix, length: length }, offset]
102
+ else
103
+ raise "Unknown SentCodeType constructor: 0x#{constructor.to_s(16)}"
104
+ end
105
+ end
106
+ end
107
+
108
+ module CodeType
109
+ def self.deserialize_from(data)
110
+ constructor = data[0, 4].unpack1('L<')
111
+ offset = 4
112
+
113
+ case constructor
114
+ when 0x72a3158c # codeTypeSms
115
+ [{ _: :code_type_sms }, offset]
116
+ when 0x741cd3e3 # codeTypeCall
117
+ [{ _: :code_type_call }, offset]
118
+ when 0x226ccefb # codeTypeFlashCall
119
+ [{ _: :code_type_flash_call }, offset]
120
+ when 0xd61ad6ee # codeTypeMissedCall
121
+ [{ _: :code_type_missed_call }, offset]
122
+ else
123
+ raise "Unknown CodeType constructor: 0x#{constructor.to_s(16)}"
124
+ end
125
+ end
126
+ end
127
+ end
128
+ 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
@@ -17,7 +17,7 @@ module MTProto
17
17
  @socket = nil
18
18
  end
19
19
 
20
- def connect
20
+ def connect!
21
21
  return if connected?
22
22
 
23
23
  @socket = TCPSocket.new(@host, @port)
@@ -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.6'
5
5
  end
data/lib/mtproto.rb CHANGED
@@ -1,11 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'mtproto/version'
4
+ require_relative 'mtproto/errors'
4
5
  require_relative 'mtproto/transport/abridged_packet_codec'
5
6
  require_relative 'mtproto/transport/tcp_connection'
6
7
  require_relative 'mtproto/tl/message'
8
+ require_relative 'mtproto/tl/serializer'
9
+ require_relative 'mtproto/tl/p_q_inner_data'
10
+ require_relative 'mtproto/tl/server_dh_inner_data'
11
+ require_relative 'mtproto/tl/client_dh_inner_data'
12
+ require_relative 'mtproto/tl/bad_msg_notification'
13
+ require_relative 'mtproto/tl/msg_container'
14
+ require_relative 'mtproto/tl/new_session_created'
15
+ require_relative 'mtproto/tl/rpc_error'
16
+ require_relative 'mtproto/tl/gzip_packed'
17
+ require_relative 'mtproto/tl/config'
18
+ require_relative 'mtproto/tl/code_settings'
19
+ require_relative 'mtproto/tl/sent_code'
7
20
  require_relative 'mtproto/crypto/rsa_key'
21
+ require_relative 'mtproto/crypto/factorization'
22
+ require_relative 'mtproto/crypto/aes_ige'
23
+ require_relative 'mtproto/crypto/rsa_pad'
24
+ require_relative 'mtproto/crypto/auth_key_helper'
25
+ require_relative 'mtproto/crypto/dh_validator'
26
+ require_relative 'mtproto/crypto/dh_key_exchange'
27
+ require_relative 'mtproto/crypto/message_key'
28
+ require_relative 'mtproto/auth_key_generator'
29
+ require_relative 'mtproto/session'
30
+ require_relative 'mtproto/encrypted_message'
8
31
  require_relative 'mtproto/client'
32
+ require_relative 'mtproto/connection'
9
33
 
10
34
  module MTProto
11
35
  end
data/tmp/.keep ADDED
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mtproto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Artem Levenkov
@@ -17,15 +17,47 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - ".env.example"
20
21
  - ".ruby-version"
21
22
  - Rakefile
23
+ - ext/aes_ige/Makefile
24
+ - ext/aes_ige/aes_ige.c
25
+ - ext/aes_ige/extconf.rb
26
+ - ext/factorization/Makefile
27
+ - ext/factorization/extconf.rb
28
+ - ext/factorization/factorization.c
22
29
  - lib/mtproto.rb
30
+ - lib/mtproto/auth_key_generator.rb
23
31
  - lib/mtproto/client.rb
32
+ - lib/mtproto/connection.rb
33
+ - lib/mtproto/crypto/aes_ige.rb
34
+ - lib/mtproto/crypto/auth_key_helper.rb
35
+ - lib/mtproto/crypto/dh_key_exchange.rb
36
+ - lib/mtproto/crypto/dh_validator.rb
37
+ - lib/mtproto/crypto/factorization.rb
38
+ - lib/mtproto/crypto/message_key.rb
24
39
  - lib/mtproto/crypto/rsa_key.rb
40
+ - lib/mtproto/crypto/rsa_pad.rb
41
+ - lib/mtproto/encrypted_message.rb
42
+ - lib/mtproto/errors.rb
43
+ - lib/mtproto/session.rb
44
+ - lib/mtproto/tl/bad_msg_notification.rb
45
+ - lib/mtproto/tl/client_dh_inner_data.rb
46
+ - lib/mtproto/tl/code_settings.rb
47
+ - lib/mtproto/tl/config.rb
48
+ - lib/mtproto/tl/gzip_packed.rb
25
49
  - lib/mtproto/tl/message.rb
50
+ - lib/mtproto/tl/msg_container.rb
51
+ - lib/mtproto/tl/new_session_created.rb
52
+ - lib/mtproto/tl/p_q_inner_data.rb
53
+ - lib/mtproto/tl/rpc_error.rb
54
+ - lib/mtproto/tl/sent_code.rb
55
+ - lib/mtproto/tl/serializer.rb
56
+ - lib/mtproto/tl/server_dh_inner_data.rb
26
57
  - lib/mtproto/transport/abridged_packet_codec.rb
27
58
  - lib/mtproto/transport/tcp_connection.rb
28
59
  - lib/mtproto/version.rb
60
+ - tmp/.keep
29
61
  homepage: https://github.com/alev-pro/mtproto-ruby
30
62
  licenses:
31
63
  - MIT