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,273 @@
1
+
2
+ SHELL = /bin/sh
3
+
4
+ # V=0 quiet, V=1 verbose. other values don't work.
5
+ V = 0
6
+ V0 = $(V:0=)
7
+ Q1 = $(V:1=)
8
+ Q = $(Q1:0=@)
9
+ ECHO1 = $(V:1=@ :)
10
+ ECHO = $(ECHO1:0=@ echo)
11
+ NULLCMD = :
12
+
13
+ #### Start of system configuration section. ####
14
+
15
+ srcdir = .
16
+ topdir = /Users/alev/.rvm/rubies/ruby-3.4.4/include/ruby-3.4.0
17
+ hdrdir = $(topdir)
18
+ arch_hdrdir = /Users/alev/.rvm/rubies/ruby-3.4.4/include/ruby-3.4.0/arm64-darwin24
19
+ PATH_SEPARATOR = :
20
+ VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
21
+ prefix = $(DESTDIR)/Users/alev/.rvm/rubies/ruby-3.4.4
22
+ rubysitearchprefix = $(rubylibprefix)/$(sitearch)
23
+ rubyarchprefix = $(rubylibprefix)/$(arch)
24
+ rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
25
+ exec_prefix = $(prefix)
26
+ vendorarchhdrdir = $(vendorhdrdir)/$(sitearch)
27
+ sitearchhdrdir = $(sitehdrdir)/$(sitearch)
28
+ rubyarchhdrdir = $(rubyhdrdir)/$(arch)
29
+ vendorhdrdir = $(rubyhdrdir)/vendor_ruby
30
+ sitehdrdir = $(rubyhdrdir)/site_ruby
31
+ rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
32
+ vendorarchdir = $(vendorlibdir)/$(sitearch)
33
+ vendorlibdir = $(vendordir)/$(ruby_version)
34
+ vendordir = $(rubylibprefix)/vendor_ruby
35
+ sitearchdir = $(sitelibdir)/$(sitearch)
36
+ sitelibdir = $(sitedir)/$(ruby_version)
37
+ sitedir = $(rubylibprefix)/site_ruby
38
+ rubyarchdir = $(rubylibdir)/$(arch)
39
+ rubylibdir = $(rubylibprefix)/$(ruby_version)
40
+ sitearchincludedir = $(includedir)/$(sitearch)
41
+ archincludedir = $(includedir)/$(arch)
42
+ sitearchlibdir = $(libdir)/$(sitearch)
43
+ archlibdir = $(libdir)/$(arch)
44
+ ridir = $(datarootdir)/$(RI_BASE_NAME)
45
+ modular_gc_dir = $(DESTDIR)
46
+ mandir = $(datarootdir)/man
47
+ localedir = $(datarootdir)/locale
48
+ libdir = $(exec_prefix)/lib
49
+ psdir = $(docdir)
50
+ pdfdir = $(docdir)
51
+ dvidir = $(docdir)
52
+ htmldir = $(docdir)
53
+ infodir = $(datarootdir)/info
54
+ docdir = $(datarootdir)/doc/$(PACKAGE)
55
+ oldincludedir = $(DESTDIR)/usr/include
56
+ includedir = $(SDKROOT)$(prefix)/include
57
+ runstatedir = $(localstatedir)/run
58
+ localstatedir = $(prefix)/var
59
+ sharedstatedir = $(prefix)/com
60
+ sysconfdir = $(prefix)/etc
61
+ datadir = $(datarootdir)
62
+ datarootdir = $(prefix)/share
63
+ libexecdir = $(exec_prefix)/libexec
64
+ sbindir = $(exec_prefix)/sbin
65
+ bindir = $(exec_prefix)/bin
66
+ archdir = $(rubyarchdir)
67
+
68
+
69
+ CC_WRAPPER =
70
+ CC = gcc
71
+ CXX = g++
72
+ LIBRUBY = $(LIBRUBY_SO)
73
+ LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
74
+ LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
75
+ LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static -framework CoreFoundation $(MAINLIBS)
76
+ empty =
77
+ OUTFLAG = -o $(empty)
78
+ COUTFLAG = -o $(empty)
79
+ CSRCFLAG = $(empty)
80
+
81
+ RUBY_EXTCONF_H =
82
+ cflags = $(hardenflags) -fdeclspec $(optflags) $(debugflags) $(warnflags)
83
+ cxxflags =
84
+ optflags = -O3 -fno-fast-math
85
+ debugflags = -ggdb3
86
+ warnflags = -Wall -Wextra -Wextra-tokens -Wdeprecated-declarations -Wdivision-by-zero -Wdiv-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wold-style-definition -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wmisleading-indentation -Wundef
87
+ cppflags =
88
+ CCDLFLAGS = -fno-common
89
+ CFLAGS = $(CCDLFLAGS) -O3 -I/opt/homebrew/opt/libyaml/include -I/opt/homebrew/opt/libksba/include -I/opt/homebrew/opt/readline/include -I/opt/homebrew/opt/zlib/include -I/opt/homebrew/opt/openssl@1.1/include $(cflags) -fno-common -pipe $(ARCH_FLAG)
90
+ INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
91
+ DEFS =
92
+ CPPFLAGS = -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT $(DEFS) $(cppflags)
93
+ CXXFLAGS = $(CCDLFLAGS) -fdeclspec $(ARCH_FLAG)
94
+ ldflags = -L. -L/opt/homebrew/opt/libyaml/lib -L/opt/homebrew/opt/libksba/lib -L/opt/homebrew/opt/readline/lib -L/opt/homebrew/opt/zlib/lib -L/opt/homebrew/opt/openssl@1.1/lib -fstack-protector-strong
95
+ dldflags = -L/opt/homebrew/opt/libyaml/lib -L/opt/homebrew/opt/libksba/lib -L/opt/homebrew/opt/readline/lib -L/opt/homebrew/opt/zlib/lib -L/opt/homebrew/opt/openssl@1.1/lib -Wl,-undefined,dynamic_lookup
96
+ ARCH_FLAG = -arch arm64
97
+ DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
98
+ LDSHARED = $(CC) -dynamic -bundle
99
+ LDSHAREDXX = $(CXX) -dynamic -bundle
100
+ POSTLINK = dsymutil $@ 2>/dev/null; { test -z '$(RUBY_CODESIGN)' || codesign -s '$(RUBY_CODESIGN)' $@; }
101
+ AR = ar
102
+ LD = ld
103
+ EXEEXT =
104
+
105
+ RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)
106
+ RUBY_SO_NAME = ruby.3.4
107
+ RUBYW_INSTALL_NAME =
108
+ RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
109
+ RUBYW_BASE_NAME = rubyw
110
+ RUBY_BASE_NAME = ruby
111
+
112
+ arch = arm64-darwin24
113
+ sitearch = $(arch)
114
+ ruby_version = 3.4.0
115
+ ruby = $(bindir)/$(RUBY_BASE_NAME)
116
+ RUBY = $(ruby)
117
+ BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME)
118
+ ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
119
+
120
+ RM = rm -f
121
+ RM_RF = rm -fr
122
+ RMDIRS = rmdir -p
123
+ MAKEDIRS = /opt/homebrew/opt/coreutils/bin/gmkdir -p
124
+ INSTALL = /opt/homebrew/opt/coreutils/bin/ginstall -c
125
+ INSTALL_PROG = $(INSTALL) -m 0755
126
+ INSTALL_DATA = $(INSTALL) -m 644
127
+ COPY = cp
128
+ TOUCH = exit >
129
+
130
+ #### End of system configuration section. ####
131
+
132
+ preload =
133
+ libpath = . $(libdir)
134
+ LIBPATH = -L. -L$(libdir)
135
+ DEFFILE =
136
+
137
+ CLEANFILES = mkmf.log
138
+ DISTCLEANFILES =
139
+ DISTCLEANDIRS =
140
+
141
+ extout =
142
+ extout_prefix =
143
+ target_prefix = /factorization
144
+ LOCAL_LIBS =
145
+ LIBS = $(LIBRUBYARG_SHARED) -lpthread
146
+ ORIG_SRCS = factorization.c
147
+ SRCS = $(ORIG_SRCS)
148
+ OBJS = factorization.o
149
+ HDRS =
150
+ LOCAL_HDRS =
151
+ TARGET = factorization
152
+ TARGET_NAME = factorization
153
+ TARGET_ENTRY = Init_$(TARGET_NAME)
154
+ DLLIB = $(TARGET).bundle
155
+ EXTSTATIC =
156
+ STATIC_LIB =
157
+
158
+ TIMESTAMP_DIR = .
159
+ BINDIR = $(bindir)
160
+ RUBYCOMMONDIR = $(sitedir)$(target_prefix)
161
+ RUBYLIBDIR = $(sitelibdir)$(target_prefix)
162
+ RUBYARCHDIR = $(sitearchdir)$(target_prefix)
163
+ HDRDIR = $(sitehdrdir)$(target_prefix)
164
+ ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix)
165
+ TARGET_SO_DIR =
166
+ TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
167
+ CLEANLIBS = $(TARGET_SO) $(TARGET_SO:=.dSYM)
168
+ CLEANOBJS = $(OBJS) *.bak
169
+ TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.-.factorization.time
170
+
171
+ all: $(DLLIB)
172
+ static: $(STATIC_LIB)
173
+ .PHONY: all install static install-so install-rb
174
+ .PHONY: clean clean-so clean-static clean-rb
175
+
176
+ clean-static::
177
+ clean-rb-default::
178
+ clean-rb::
179
+ clean-so::
180
+ clean: clean-so clean-static clean-rb-default clean-rb
181
+ -$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
182
+
183
+ distclean-rb-default::
184
+ distclean-rb::
185
+ distclean-so::
186
+ distclean-static::
187
+ distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
188
+ -$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
189
+ -$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
190
+ -$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
191
+
192
+ realclean: distclean
193
+ install: install-so install-rb
194
+
195
+ install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP)
196
+ $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
197
+ clean-static::
198
+ -$(Q)$(RM) $(STATIC_LIB)
199
+ install-rb: pre-install-rb do-install-rb install-rb-default
200
+ install-rb-default: pre-install-rb-default do-install-rb-default
201
+ pre-install-rb: Makefile
202
+ pre-install-rb-default: Makefile
203
+ do-install-rb:
204
+ do-install-rb-default:
205
+ pre-install-rb-default:
206
+ @$(NULLCMD)
207
+ $(TARGET_SO_DIR_TIMESTAMP):
208
+ $(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
209
+ $(Q) $(TOUCH) $@
210
+
211
+ site-install: site-install-so site-install-rb
212
+ site-install-so: install-so
213
+ site-install-rb: install-rb
214
+
215
+ .SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
216
+
217
+ .cc.o:
218
+ $(ECHO) compiling $(<)
219
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
220
+
221
+ .cc.S:
222
+ $(ECHO) translating $(<)
223
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
224
+
225
+ .mm.o:
226
+ $(ECHO) compiling $(<)
227
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
228
+
229
+ .mm.S:
230
+ $(ECHO) translating $(<)
231
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
232
+
233
+ .cxx.o:
234
+ $(ECHO) compiling $(<)
235
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
236
+
237
+ .cxx.S:
238
+ $(ECHO) translating $(<)
239
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
240
+
241
+ .cpp.o:
242
+ $(ECHO) compiling $(<)
243
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
244
+
245
+ .cpp.S:
246
+ $(ECHO) translating $(<)
247
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
248
+
249
+ .c.o:
250
+ $(ECHO) compiling $(<)
251
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
252
+
253
+ .c.S:
254
+ $(ECHO) translating $(<)
255
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
256
+
257
+ .m.o:
258
+ $(ECHO) compiling $(<)
259
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
260
+
261
+ .m.S:
262
+ $(ECHO) translating $(<)
263
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
264
+
265
+ $(TARGET_SO): $(OBJS) Makefile
266
+ $(ECHO) linking shared-object factorization/$(DLLIB)
267
+ -$(Q)$(RM) $(@)
268
+ $(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
269
+ $(Q) $(POSTLINK)
270
+
271
+
272
+
273
+ $(OBJS): $(HDRS) $(ruby_headers)
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+
3
+ create_makefile('factorization/factorization')
@@ -0,0 +1,62 @@
1
+ #include <ruby.h>
2
+ #include <stdint.h>
3
+ #include <math.h>
4
+
5
+ static VALUE mMTProto;
6
+ static VALUE mCrypto;
7
+ static VALUE mFactorization;
8
+
9
+ static VALUE
10
+ factorize_pq(VALUE self, VALUE pq_bytes)
11
+ {
12
+ Check_Type(pq_bytes, T_STRING);
13
+
14
+ long pq_len = RSTRING_LEN(pq_bytes);
15
+ unsigned char *pq_ptr = (unsigned char *)RSTRING_PTR(pq_bytes);
16
+
17
+ uint64_t n = 0;
18
+ for (long i = 0; i < pq_len; i++) {
19
+ n = (n << 8) | pq_ptr[i];
20
+ }
21
+
22
+ if (n <= 3) {
23
+ rb_raise(rb_eArgError, "Number must be > 3");
24
+ }
25
+
26
+ if (n % 2 == 0) {
27
+ VALUE result = rb_ary_new2(2);
28
+ rb_ary_push(result, ULL2NUM(2));
29
+ rb_ary_push(result, ULL2NUM(n / 2));
30
+ return result;
31
+ }
32
+
33
+ uint64_t limit = (uint64_t)sqrt((double)n) + 1;
34
+ for (uint64_t i = 3; i < limit; i += 2) {
35
+ if (n % i == 0) {
36
+ uint64_t p = i;
37
+ uint64_t q = n / i;
38
+
39
+ if (p > q) {
40
+ uint64_t tmp = p;
41
+ p = q;
42
+ q = tmp;
43
+ }
44
+
45
+ VALUE result = rb_ary_new2(2);
46
+ rb_ary_push(result, ULL2NUM(p));
47
+ rb_ary_push(result, ULL2NUM(q));
48
+ return result;
49
+ }
50
+ }
51
+
52
+ rb_raise(rb_eRuntimeError, "No non-trivial factors found (n might be prime)");
53
+ }
54
+
55
+ void Init_factorization(void)
56
+ {
57
+ mMTProto = rb_define_module("MTProto");
58
+ mCrypto = rb_define_module_under(mMTProto, "Crypto");
59
+ mFactorization = rb_define_module_under(mCrypto, "FactorizationExt");
60
+
61
+ rb_define_singleton_method(mFactorization, "factorize_pq", factorize_pq, 1);
62
+ }
@@ -0,0 +1,228 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'digest'
5
+
6
+ module MTProto
7
+ class AuthKeyGenerator
8
+ attr_reader :connection, :auth_key, :server_salt, :time_offset
9
+
10
+ def initialize(connection)
11
+ @connection = connection
12
+ @auth_key = nil
13
+ @server_salt = nil
14
+ @time_offset = 0
15
+ end
16
+
17
+ def generate
18
+ res_pq = req_pq_multi
19
+ server_key = find_server_key(res_pq[:fingerprints])
20
+ p, q = Crypto::Factorization.factorize_pq(res_pq[:pq])
21
+ new_nonce = SecureRandom.random_bytes(32)
22
+
23
+ encrypted_data = encrypt_pq_inner_data(res_pq, p, q, server_key, new_nonce)
24
+ server_dh_params = send_req_dh_params(res_pq, p, q, server_key, encrypted_data)
25
+ server_dh_inner_data = decrypt_server_dh_params(res_pq, new_nonce, server_dh_params)
26
+
27
+ Crypto::DHValidator.validate_dh_params(
28
+ server_dh_inner_data.g,
29
+ server_dh_inner_data.dh_prime,
30
+ server_dh_inner_data.g_a
31
+ )
32
+
33
+ client_dh_params = Crypto::DHKeyExchange.generate_client_dh_params(
34
+ server_dh_inner_data.g,
35
+ server_dh_inner_data.dh_prime
36
+ )
37
+
38
+ auth_key = Crypto::DHKeyExchange.compute_auth_key(
39
+ server_dh_inner_data.g_a,
40
+ client_dh_params[:b],
41
+ server_dh_inner_data.dh_prime
42
+ )
43
+
44
+ tmp_aes_key = Crypto::AuthKeyHelper.derive_tmp_aes_key(new_nonce, res_pq[:server_nonce])
45
+ tmp_aes_iv = Crypto::AuthKeyHelper.derive_tmp_aes_iv(new_nonce, res_pq[:server_nonce])
46
+
47
+ dh_gen_response = send_client_dh_params(
48
+ res_pq,
49
+ new_nonce,
50
+ client_dh_params,
51
+ tmp_aes_key,
52
+ tmp_aes_iv
53
+ )
54
+
55
+ verify_dh_gen_response(dh_gen_response, res_pq, new_nonce, auth_key)
56
+
57
+ @auth_key = auth_key
58
+ @server_salt = compute_server_salt(new_nonce, res_pq[:server_nonce])
59
+ @time_offset = server_dh_inner_data.server_time - Time.now.to_i
60
+
61
+ {
62
+ auth_key: @auth_key,
63
+ server_salt: @server_salt,
64
+ time_offset: @time_offset,
65
+ server_dh_params: server_dh_params,
66
+ server_dh_inner_data: server_dh_inner_data,
67
+ client_dh_params: client_dh_params,
68
+ dh_gen_response: dh_gen_response
69
+ }
70
+ end
71
+
72
+ private
73
+
74
+ def req_pq_multi
75
+ nonce = SecureRandom.random_bytes(16)
76
+ message = TL::Message.req_pq_multi(nonce)
77
+ @connection.send(message.serialize)
78
+
79
+ response_data = @connection.recv(timeout: 10)
80
+ response_message = TL::Message.deserialize(response_data)
81
+ res_pq = response_message.parse_res_pq
82
+
83
+ raise 'Nonce mismatch!' unless res_pq[:nonce] == nonce
84
+
85
+ res_pq
86
+ end
87
+
88
+ def find_server_key(fingerprints)
89
+ server_key = Crypto::RSAKey.find_by_fingerprint(fingerprints)
90
+ raise 'No matching RSA key found!' unless server_key
91
+
92
+ server_key
93
+ end
94
+
95
+ def encrypt_pq_inner_data(res_pq, p, q, server_key, new_nonce)
96
+ inner_data = TL::PQInnerData.new(
97
+ pq: Crypto::Factorization.bytes_to_integer(res_pq[:pq]),
98
+ p: p,
99
+ q: q,
100
+ nonce: res_pq[:nonce],
101
+ server_nonce: res_pq[:server_nonce],
102
+ new_nonce: new_nonce,
103
+ dc: @connection.port == 443 ? 2 : 1
104
+ )
105
+
106
+ Crypto::RSA_PAD.encrypt(inner_data.serialize, server_key)
107
+ end
108
+
109
+ def send_req_dh_params(res_pq, p, q, server_key, encrypted_data)
110
+ message = TL::Message.req_DH_params(
111
+ nonce: res_pq[:nonce],
112
+ server_nonce: res_pq[:server_nonce],
113
+ p: p,
114
+ q: q,
115
+ public_key_fingerprint: server_key.fingerprint,
116
+ encrypted_data: encrypted_data
117
+ )
118
+
119
+ @connection.send(message.serialize)
120
+
121
+ response_data = @connection.recv(timeout: 10)
122
+ response_message = TL::Message.deserialize(response_data)
123
+ server_dh_params = response_message.parse_server_DH_params_ok
124
+
125
+ raise 'Nonce mismatch!' unless server_dh_params[:nonce] == res_pq[:nonce]
126
+ raise 'Server nonce mismatch!' unless server_dh_params[:server_nonce] == res_pq[:server_nonce]
127
+
128
+ server_dh_params
129
+ end
130
+
131
+ def decrypt_server_dh_params(res_pq, new_nonce, server_dh_params)
132
+ tmp_aes_key = Crypto::AuthKeyHelper.derive_tmp_aes_key(new_nonce, res_pq[:server_nonce])
133
+ tmp_aes_iv = Crypto::AuthKeyHelper.derive_tmp_aes_iv(new_nonce, res_pq[:server_nonce])
134
+
135
+ answer_with_hash = Crypto::AES_IGE.decrypt_ige(
136
+ server_dh_params[:encrypted_answer],
137
+ tmp_aes_key,
138
+ tmp_aes_iv
139
+ )
140
+
141
+ answer_hash = answer_with_hash[0, 20]
142
+ answer = answer_with_hash[20..]
143
+
144
+ loop do
145
+ computed_hash = Digest::SHA1.digest(answer)
146
+ break if answer_hash == computed_hash
147
+
148
+ raise 'Answer hash mismatch!' if answer.bytesize <= 1
149
+
150
+ answer = answer[0..-2]
151
+ end
152
+
153
+ server_dh_inner_data = TL::ServerDHInnerData.deserialize(answer)
154
+
155
+ raise 'Nonce mismatch in DH inner data!' unless server_dh_inner_data.nonce == res_pq[:nonce]
156
+ raise 'Server nonce mismatch in DH inner data!' unless server_dh_inner_data.server_nonce == res_pq[:server_nonce]
157
+
158
+ server_dh_inner_data
159
+ end
160
+
161
+ def send_client_dh_params(res_pq, new_nonce, client_dh_params, tmp_aes_key, tmp_aes_iv)
162
+ client_dh_inner_data = TL::ClientDHInnerData.new(
163
+ nonce: res_pq[:nonce],
164
+ server_nonce: res_pq[:server_nonce],
165
+ retry_id: 0,
166
+ g_b: client_dh_params[:g_b_bytes]
167
+ )
168
+
169
+ client_dh_data = client_dh_inner_data.serialize
170
+ client_dh_data_with_hash = Digest::SHA1.digest(client_dh_data) + client_dh_data
171
+
172
+ padding_length = (16 - (client_dh_data_with_hash.bytesize % 16)) % 16
173
+ client_dh_data_with_hash += SecureRandom.random_bytes(padding_length) if padding_length.positive?
174
+
175
+ client_dh_encrypted = Crypto::AES_IGE.encrypt_ige(
176
+ client_dh_data_with_hash,
177
+ tmp_aes_key,
178
+ tmp_aes_iv
179
+ )
180
+
181
+ message = TL::Message.set_client_DH_params(
182
+ nonce: res_pq[:nonce],
183
+ server_nonce: res_pq[:server_nonce],
184
+ encrypted_data: client_dh_encrypted
185
+ )
186
+
187
+ @connection.send(message.serialize)
188
+
189
+ response_data = @connection.recv(timeout: 10)
190
+ response_message = TL::Message.deserialize(response_data)
191
+ response_message.parse_dh_gen_response
192
+ end
193
+
194
+ def verify_dh_gen_response(dh_gen_response, res_pq, new_nonce, auth_key)
195
+ raise 'Nonce mismatch in DH response!' unless dh_gen_response[:nonce] == res_pq[:nonce]
196
+ raise 'Server nonce mismatch in DH response!' unless dh_gen_response[:server_nonce] == res_pq[:server_nonce]
197
+
198
+ auth_key_hash = Digest::SHA1.digest(auth_key)
199
+ auth_key_aux_hash = auth_key_hash[0, 8]
200
+
201
+ expected_new_nonce_hash = case dh_gen_response[:status]
202
+ when :ok
203
+ Digest::SHA1.digest(new_nonce + "\x01".b + auth_key_aux_hash)[-16..]
204
+ when :retry
205
+ Digest::SHA1.digest(new_nonce + "\x02".b + auth_key_aux_hash)[-16..]
206
+ when :fail
207
+ Digest::SHA1.digest(new_nonce + "\x03".b + auth_key_aux_hash)[-16..]
208
+ end
209
+
210
+ raise 'new_nonce_hash mismatch!' unless dh_gen_response[:new_nonce_hash] == expected_new_nonce_hash
211
+
212
+ case dh_gen_response[:status]
213
+ when :retry
214
+ raise 'Server requested retry - not implemented'
215
+ when :fail
216
+ raise 'Server reported DH generation failure'
217
+ end
218
+ end
219
+
220
+ def compute_server_salt(new_nonce, server_nonce)
221
+ server_salt_bytes = String.new(capacity: 8)
222
+ 8.times do |i|
223
+ server_salt_bytes << (new_nonce[i].ord ^ server_nonce[i].ord)
224
+ end
225
+ server_salt_bytes.unpack1('Q<')
226
+ end
227
+ end
228
+ end