Class: ShadowsocksRuby::Protocols::TlsTicketProtocol
- Inherits:
-
Object
- Object
- ShadowsocksRuby::Protocols::TlsTicketProtocol
- Includes:
- BufferHelper, DummyHelper
- Defined in:
- lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb
Overview
TLS 1.2 Obfuscation Protocol
Specification:
-
github.com/shadowsocksr/shadowsocksr/blob/manyuser/shadowsocks/obfsplugin/obfs_tls.py
-
github.com/shadowsocksr/obfsplugin/blob/master/c/tls1.2_ticket.c
TLS 1.2 Reference:
Constant Summary collapse
- VERSION_SSL_3_0 =
[3, 0]
- VERSION_TLS_1_0 =
[3, 1]
- VERSION_TLS_1_1 =
[3, 2]
- VERSION_TLS_1_2 =
[3, 3]
- CTYPE_ChangeCipherSpec =
Content types
0x14
- CTYPE_Alert =
0x15
- CTYPE_Handshake =
0x16
- CTYPE_Application =
0x17
- CTYPE_Heartbeat =
0x18
- MTYPE_HelloRequest =
Message types
0
- MTYPE_ClientHello =
1
- MTYPE_ServerHello =
2
- MTYPE_NewSessionTicket =
4
- MTYPE_Certificate =
11
- MTYPE_ServerKeyExchange =
12
- MTYPE_CertificateRequest =
13
- MTYPE_ServerHelloDone =
14
- MTYPE_CertificateVerify =
15
- MTYPE_ClientKeyExchange =
16
- MTYPE_Finished =
20
Instance Attribute Summary collapse
-
#next_protocol ⇒ Object
Returns the value of attribute next_protocol.
Instance Method Summary collapse
-
#get_random ⇒ Object
helpers.
-
#initialize(params = {}) ⇒ TlsTicketProtocol
constructor
A new instance of TlsTicketProtocol.
- #make_ext_sni(host) ⇒ Object
- #recv_client_change_cipherspec_and_finish ⇒ Object
- #recv_client_hello ⇒ Object
- #recv_server_hello ⇒ Object
- #send_client_change_cipherspec_and_finish(data) ⇒ Object
- #send_client_hello ⇒ Object
- #send_data_application_pharse(data) ⇒ Object
- #send_server_hello ⇒ Object
- #tcp_receive_from_localbackend_first_packet(n) ⇒ Object (also: #tcp_receive_from_localbackend)
-
#tcp_receive_from_localbackend_other_packet(n) ⇒ Object
TLS 1.2 Application Pharse.
- #tcp_receive_from_remoteserver_first_packet(n) ⇒ Object (also: #tcp_receive_from_remoteserver)
-
#tcp_receive_from_remoteserver_other_packet(n) ⇒ Object
TLS 1.2 Application Pharse.
- #tcp_send_to_localbackend(data) ⇒ Object
- #tcp_send_to_remoteserver_first_packet(data) ⇒ Object (also: #tcp_send_to_remoteserver)
-
#tcp_send_to_remoteserver_other_packet(data) ⇒ Object
TLS 1.2 Application Pharse.
Methods included from BufferHelper
#tcp_receive_from_localbackend_in_buffer, #tcp_receive_from_localbackend_other_packet_helper, #tcp_receive_from_remoteserver_in_buffer, #tcp_receive_from_remoteserver_other_packet_helper
Methods included from DummyHelper
#async_recv, #raise_me, #send_data
Constructor Details
#initialize(params = {}) ⇒ TlsTicketProtocol
Returns a new instance of TlsTicketProtocol.
53 54 55 56 57 58 59 60 61 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 53 def initialize params = {} @params = {:compatible => true}.merge(params) @buffer = '' @client_id = Random.new.bytes(32) @max_time_dif = 60 * 60 * 24 # time dif (second) setting @startup_time = Time.now.to_i - 60 * 30 @client_data = @params[:lrucache] || LRUCache.new(:ttl => 60 * 5) @connected = EM::DefaultDeferrable.new end |
Instance Attribute Details
#next_protocol ⇒ Object
Returns the value of attribute next_protocol.
45 46 47 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 45 def next_protocol @next_protocol end |
Instance Method Details
#get_random ⇒ Object
helpers
148 149 150 151 152 153 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 148 def get_random verifyid = [Time.now.to_i].pack("N") << Random.new.bytes(18) hello = "" hello << verifyid # Random part 1 hello << ShadowsocksRuby::Cipher.hmac_sha1_digest(@params[:key] + @client_id, verifyid) # Random part 2 end |
#make_ext_sni(host) ⇒ Object
314 315 316 317 318 319 320 321 322 323 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 314 def make_ext_sni host name_type = 0 #host_name server_name = [name_type, host.length].pack("Cn") << host server_name_list = [server_name.length].pack("n") << server_name type = Util.hex2bin("0000") data = [server_name_list.length].pack("n") << server_name_list return type << data end |
#recv_client_change_cipherspec_and_finish ⇒ Object
289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 289 def recv_client_change_cipherspec_and_finish data = async_recv 43 if data[0, 6] != [CTYPE_ChangeCipherSpec, *VERSION_TLS_1_2, 0, 1, 1].pack("C*") # ChangeCipherSpec raise PharseError, "server_decode data error" end if data[6, 5] != [CTYPE_Handshake, *VERSION_TLS_1_2, 32].pack("C3n") # Finished raise PharseError, "server_decode data error" end if ShadowsocksRuby::Cipher.hmac_sha1_digest(@params[:key] + @client_id, data[0, 33]) != data[33, 10] raise PharseError, "server_decode data error" end end |
#recv_client_hello ⇒ Object
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 214 def recv_client_hello data = async_recv 3 if data != [CTYPE_Handshake, *VERSION_TLS_1_0].pack("C3") if @params[:compatible] @buffer = data @no_effect = true return else raise PharseError, "decode error" end end = async_recv(2).unpack("n")[0] = async_recv() if (.slice!(0, 2) != [MTYPE_ClientHello, 0].pack("C2")) raise PharseError, "tls_auth not client hello message" end len_client_hello = .slice!(0, 2).unpack("n")[0] client_hello = if (len_client_hello != client_hello.length ) raise PharseError, "tls_auth wrong message size" end if (client_hello.slice!(0,2) != [*VERSION_TLS_1_2].pack("C2")) raise PharseError, "tls_auth wrong tls version" end verifyid = client_hello.slice!(0, 32) len_sessionid = client_hello.slice!(0,1).unpack("C")[0] if (len_sessionid < 32) raise PharseError, "tls_auth wrong sessionid_len" end sessionid = client_hello.slice!(0, len_sessionid) @client_id = sessionid sha1 = ShadowsocksRuby::Cipher::hmac_sha1_digest(@params[:key] + sessionid, verifyid[0, 22]) utc_time = Time.at(verifyid[0, 4].unpack("N")[0]) time_dif = Time.now.to_i - utc_time.to_i #if @params[:obfs_param] != nil # @max_time_dif = @params[:obfs_param].to_i #end if @max_time_dif > 0 && (time_dif.abs > @max_time_dif or utc_time.to_i - @startup_time < - @max_time_dif / 2) raise PharseError, "tls_auth wrong time" end if sha1 != verifyid[22 .. -1] raise PharseError, "tls_auth wrong sha1" end if @client_data[verifyid[0, 22]] raise PharseError, "replay attack detect, id = #{Util.bin2hex(verifyid)}" end @client_data[verifyid[0, 22]] = sessionid end |
#recv_server_hello ⇒ Object
206 207 208 209 210 211 212 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 206 def recv_server_hello data = async_recv(129) # ServerHello 76 + ServerChangeSipherSpec 6 + Finished 37 verify = data[11 ... 33] if ShadowsocksRuby::Cipher.hmac_sha1_digest(@params[:key] + @client_id, verify) != data[33 ... 43] raise PharseError, "client_decode data error" end end |
#send_client_change_cipherspec_and_finish(data) ⇒ Object
197 198 199 200 201 202 203 204 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 197 def send_client_change_cipherspec_and_finish data buf = "" buf << [CTYPE_ChangeCipherSpec, *VERSION_TLS_1_2, 0, 1, 1].pack("C*") buf << [CTYPE_Handshake, *VERSION_TLS_1_2, 32].pack("C3n") << Random.new.bytes(22) buf << ShadowsocksRuby::Cipher::hmac_sha1_digest(@params[:key] + @client_id, buf) buf << [CTYPE_Application, *VERSION_TLS_1_2, data.length].pack("C3n") << data send_data buf end |
#send_client_hello ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 155 def send_client_hello client_hello = "" client_hello << [*VERSION_TLS_1_2].pack("C2") # ProtocolVersion client_hello << get_random # Random len 32 client_hello << [32].pack("C") << @client_id # SessionID client_hello << Util.hex2bin("001cc02bc02fcca9cca8cc14cc13c00ac014c009c013009c0035002f000a") # CipherSuite client_hello << Util.hex2bin("0100") # CompressionMethod ext = Util.hex2bin("ff01000100") # Extension 1 (type ff01 + len 0001 + data 00 ) hosts = @params[:obfs_param] || @params[:host] if (hosts == nil or hosts == "") raise PharseError, "No :host or :obfs_param parameters" end if (("0".."9").include? hosts[-1]) hosts = "" end hosts = hosts.split(",") if hosts.length != 0 host = hosts[Random.rand(hosts.length)] else host = "" end ext << make_ext_sni(host) # Extension 2 ext << Util.hex2bin("00170000") # Extension 3 (type 0017 + len 0000) ext << Util.hex2bin("002300d0") << Random.new.bytes(208) # ticket, Extension 4 (type 0023 + len 00d0 + data) ext << Util.hex2bin("000d001600140601060305010503040104030301030302010203") # Extension 5 (type 000d + len 0016 + data) ext << Util.hex2bin("000500050100000000") # Extension 6 (type 0005 + len 0005 + data) ext << Util.hex2bin("00120000") # Extension 7 (type 0012 + len 0000) ext << Util.hex2bin("75500000") # Extension 8 (type 7550 + len 0000) ext << Util.hex2bin("000b00020100") # Extension 9 (type 000b + len 0002 + data) ext << Util.hex2bin("000a0006000400170018") # Extension 10 (type 000a + len 0006 + data) client_hello << [ext.length].pack("n") << ext # Extension List = [MTYPE_ClientHello, 0, client_hello.length].pack("CCn") << client_hello = [CTYPE_Handshake,*VERSION_TLS_1_0, .length].pack("C3n") << send_data() end |
#send_data_application_pharse(data) ⇒ Object
302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 302 def send_data_application_pharse data buf = "" while data.length > 2048 size = [Random.rand(65535) % 4096 + 100, data.length].min buf << [CTYPE_Application, *VERSION_TLS_1_2, size].pack("C3n") << data.slice!(0, size) end if data.length > 0 buf << [CTYPE_Application, *VERSION_TLS_1_2, data.length].pack("C3n") << data end send_data buf end |
#send_server_hello ⇒ Object
275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 275 def send_server_hello data = [*VERSION_TLS_1_2].pack("C2") data << get_random data << Util.hex2bin("20") # len 32 in decimal data << @client_id data << Util.hex2bin("c02f000005ff01000100") data = Util.hex2bin("0200") << [data.length].pack("n") << data data = Util.hex2bin("160303") << [data.length].pack("n") << data # ServerHello len 86 (11 + 32 + 1 + 32 + 10) data << Util.hex2bin("14") << [*VERSION_TLS_1_2].pack("C2") << Util.hex2bin("000101") # ChangeCipherSpec len (6) data << Util.hex2bin("16") << [*VERSION_TLS_1_2].pack("C2") << Util.hex2bin("0020") << Random.new.bytes(22) data << ShadowsocksRuby::Cipher.hmac_sha1_digest(@params[:key] + @client_id, data) # Finished len(37) send_data data # len 129 end |
#tcp_receive_from_localbackend_first_packet(n) ⇒ Object Also known as: tcp_receive_from_localbackend
104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 104 def tcp_receive_from_localbackend_first_packet n class << self alias tcp_receive_from_localbackend tcp_receive_from_localbackend_other_packet end recv_client_hello @no_effect ||= nil if !@no_effect send_server_hello recv_client_change_cipherspec_and_finish tcp_receive_from_localbackend_other_packet n else tcp_receive_from_localbackend_other_packet_helper n end end |
#tcp_receive_from_localbackend_other_packet(n) ⇒ Object
TLS 1.2 Application Pharse
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 122 def tcp_receive_from_localbackend_other_packet n @no_effect ||= nil if !@no_effect begin head = async_recv 3 if head != [CTYPE_Application, *VERSION_TLS_1_2].pack("C3") raise PharseError, "server_decode appdata error" end size = async_recv(2).unpack("n")[0] end while size == 0 @buffer << async_recv(size) end tcp_receive_from_localbackend_other_packet_helper n end |
#tcp_receive_from_remoteserver_first_packet(n) ⇒ Object Also known as: tcp_receive_from_remoteserver
78 79 80 81 82 83 84 85 86 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 78 def tcp_receive_from_remoteserver_first_packet n send_client_hello recv_server_hello @connected.succeed class << self alias tcp_receive_from_remoteserver tcp_receive_from_remoteserver_other_packet end tcp_receive_from_remoteserver_other_packet n end |
#tcp_receive_from_remoteserver_other_packet(n) ⇒ Object
TLS 1.2 Application Pharse
91 92 93 94 95 96 97 98 99 100 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 91 def tcp_receive_from_remoteserver_other_packet n head = async_recv 3 if head != [CTYPE_Application, *VERSION_TLS_1_2].pack("C3") raise PharseError, "client_decode appdata error" end size = async_recv(2).unpack("n")[0] @buffer << async_recv(size) tcp_receive_from_remoteserver_other_packet_helper n end |
#tcp_send_to_localbackend(data) ⇒ Object
138 139 140 141 142 143 144 145 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 138 def tcp_send_to_localbackend data @no_effect ||= nil if !@no_effect send_data_application_pharse data else send_data data end end |
#tcp_send_to_remoteserver_first_packet(data) ⇒ Object Also known as: tcp_send_to_remoteserver
63 64 65 66 67 68 69 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 63 def tcp_send_to_remoteserver_first_packet data @connected.callback { send_client_change_cipherspec_and_finish data } class << self alias tcp_send_to_remoteserver tcp_send_to_remoteserver_other_packet end end |
#tcp_send_to_remoteserver_other_packet(data) ⇒ Object
TLS 1.2 Application Pharse
74 75 76 |
# File 'lib/shadowsocks_ruby/protocols/obfs/tls_ticket.rb', line 74 def tcp_send_to_remoteserver_other_packet data @connected.callback { send_data_application_pharse data } end |