ruby-tls 2.1.2 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a60db39059fb7d735d7aa9201631628a65cf1291
4
- data.tar.gz: dd41be2f38c92d9fedf92aec30620bead91c5c9d
3
+ metadata.gz: b85f5f85aa66d137ff1ff2bd3894adaf81d07679
4
+ data.tar.gz: e0034106218aaa3d0ecc11ff067363a364eababa
5
5
  SHA512:
6
- metadata.gz: f088fbbd7c397ec37dd60efd2a8722b933673b0e21a31003dc67e6f159567e964af3f0fcd568930be60da39546120de6dd8195c59ad3be7ff35f07eaf495260b
7
- data.tar.gz: b9293b184946062a3eef3a778c65d269bf28cd7ead8b4e1fe8fca9bfac1aa213adef0c8e20e1d7370e7d2a5036bb54c735697066bc1da45baedf68425bbfb1bc
6
+ metadata.gz: a4b103bd35d4eeb0f6e88ec95d03cdde3d282256567ac8ae6b6e2a714b8d81d16cc049475499bdca85aa4971ecfdc1f43caeafb73ef5a03509e3f93f125ac813
7
+ data.tar.gz: 5b50ebc3041e80fdba1fb24bcb55e3a3fc013e22e6ce23b5bb873badff8fa23b79c2e65818955a893081963367da21bf6c266159e02300da04dc1d254d209833
data/lib/ruby-tls.rb CHANGED
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
1
2
 
2
- require "ruby-tls/ssl" # Loads OpenSSL using FFI
3
+ require 'ruby-tls/ssl' # Loads OpenSSL using FFI
3
4
 
4
5
  module RubyTls
5
6
  end
data/lib/ruby-tls/ssl.rb CHANGED
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ffi'
2
4
  require 'ffi-compiler/loader'
3
5
  require 'thread'
4
- require 'thread_safe'
6
+ require 'concurrent'
5
7
 
6
8
 
7
9
  module RubyTls
@@ -139,6 +141,32 @@ module RubyTls
139
141
  SSL_CTX_ctrl(ssl_ctx, SSL_CTRL_SET_SESS_CACHE_SIZE, op, nil)
140
142
  end
141
143
 
144
+ attach_function :SSL_ctrl, [:ssl, :int, :long, :pointer], :long
145
+ SSL_CTRL_SET_TLSEXT_HOSTNAME = 55
146
+ def self.SSL_set_tlsext_host_name(ssl, host_name)
147
+ name = FFI::MemoryPointer.from_string(host_name)
148
+ SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, name)
149
+ end
150
+
151
+ # Server Name Indication (SNI) Support
152
+ # NOTE:: We've hard coded the callback here (SSL defines a NULL callback)
153
+ callback :ssl_servername_cb, [:ssl, :pointer, :pointer], :int
154
+ attach_function :SSL_CTX_callback_ctrl, [:ssl_ctx, :int, :ssl_servername_cb], :long
155
+ SSL_CTRL_SET_TLSEXT_SERVERNAME_CB = 53
156
+ def self.SSL_CTX_set_tlsext_servername_callback(ctx, callback)
157
+ SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, callback)
158
+ end
159
+
160
+ attach_function :SSL_get_servername, [:ssl, :int], :string
161
+ TLSEXT_NAMETYPE_host_name = 0
162
+
163
+ attach_function :SSL_set_SSL_CTX, [:ssl, :ssl_ctx], :ssl_ctx
164
+
165
+ SSL_TLSEXT_ERR_OK = 0
166
+ SSL_TLSEXT_ERR_ALERT_WARNING = 1
167
+ SSL_TLSEXT_ERR_ALERT_FATAL = 2
168
+ SSL_TLSEXT_ERR_NOACK = 3
169
+
142
170
  attach_function :SSL_CTX_use_PrivateKey_file, [:ssl_ctx, :string, :int], :int, :blocking => true
143
171
  attach_function :SSL_CTX_use_PrivateKey, [:ssl_ctx, :pointer], :int
144
172
  attach_function :ERR_print_errors_fp, [:pointer], :void # Pointer == File Handle
@@ -153,11 +181,6 @@ module RubyTls
153
181
  begin
154
182
  attach_function :SSL_CTX_set_alpn_protos, [:ssl_ctx, :string, :uint], :int
155
183
 
156
- SSL_TLSEXT_ERR_OK = 0
157
- SSL_TLSEXT_ERR_ALERT_WARNING = 1
158
- SSL_TLSEXT_ERR_ALERT_FATAL = 2
159
- SSL_TLSEXT_ERR_NOACK = 3
160
-
161
184
  OPENSSL_NPN_UNSUPPORTED = 0
162
185
  OPENSSL_NPN_NEGOTIATED = 1
163
186
  OPENSSL_NPN_NO_OVERLAP = 2
@@ -289,11 +312,11 @@ keystr
289
312
 
290
313
  class Context
291
314
  # Based on information from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
292
- CIPHERS = 'EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4'.freeze
293
- SESSION = 'ruby-tls'.freeze
315
+ CIPHERS = 'EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4'
316
+ SESSION = 'ruby-tls'
294
317
 
295
318
 
296
- ALPN_LOOKUP = ThreadSafe::Cache.new
319
+ ALPN_LOOKUP = ::Concurrent::Map.new
297
320
  ALPN_Select_CB = FFI::Function.new(:int, [
298
321
  # array of str, unit8 out,uint8 in, *arg
299
322
  :pointer, :pointer, :pointer, :string, :uint, :pointer
@@ -317,16 +340,19 @@ keystr
317
340
 
318
341
  def initialize(server, options = {})
319
342
  @is_server = server
320
- @ssl_ctx = SSL.SSL_CTX_new(server ? SSL.SSLv23_server_method : SSL.SSLv23_client_method)
321
- SSL.SSL_CTX_set_options(@ssl_ctx, SSL::SSL_OP_ALL)
322
- SSL.SSL_CTX_set_mode(@ssl_ctx, SSL::SSL_MODE_RELEASE_BUFFERS)
323
343
 
324
344
  if @is_server
345
+ @ssl_ctx = SSL.SSL_CTX_new(SSL.SSLv23_server_method)
325
346
  set_private_key(options[:private_key] || SSL::DEFAULT_PRIVATE)
326
347
  set_certificate(options[:cert_chain] || SSL::DEFAULT_CERT)
327
348
  set_client_ca(options[:client_ca])
349
+ else
350
+ @ssl_ctx = SSL.SSL_CTX_new(SSL.SSLv23_client_method)
328
351
  end
329
352
 
353
+ SSL.SSL_CTX_set_options(@ssl_ctx, SSL::SSL_OP_ALL)
354
+ SSL.SSL_CTX_set_mode(@ssl_ctx, SSL::SSL_MODE_RELEASE_BUFFERS)
355
+
330
356
  SSL.SSL_CTX_set_cipher_list(@ssl_ctx, options[:ciphers] || CIPHERS)
331
357
  @alpn_set = false
332
358
 
@@ -363,12 +389,30 @@ keystr
363
389
  attr_reader :alpn_set
364
390
  attr_reader :alpn_str
365
391
 
392
+ def add_server_name_indication
393
+ raise 'only valid for server mode context' unless @is_server
394
+ SSL.SSL_CTX_set_tlsext_servername_callback(@ssl_ctx, ServerNameCB)
395
+ end
396
+
397
+ ServerNameCB = FFI::Function.new(:int, [:pointer, :pointer, :pointer]) do |ssl, _, _|
398
+ ruby_ssl = Box::InstanceLookup[ssl.address]
399
+ return SSL::SSL_TLSEXT_ERR_NOACK unless ruby_ssl
400
+
401
+ ctx = ruby_ssl.hosts[SSL.SSL_get_servername(ssl, SSL::TLSEXT_NAMETYPE_host_name)]
402
+ if ctx
403
+ SSL.SSL_set_SSL_CTX(ssl, ctx.ssl_ctx)
404
+ SSL::SSL_TLSEXT_ERR_OK
405
+ else
406
+ SSL::SSL_TLSEXT_ERR_ALERT_FATAL
407
+ end
408
+ end
409
+
366
410
 
367
411
  private
368
412
 
369
413
 
370
414
  def self.build_alpn_string(protos)
371
- protocols = ''.force_encoding('ASCII-8BIT')
415
+ protocols = String.new.force_encoding('ASCII-8BIT')
372
416
  protos.each do |prot|
373
417
  protocol = prot.to_s
374
418
  protocols << protocol.length
@@ -427,7 +471,7 @@ keystr
427
471
 
428
472
 
429
473
  class Box
430
- InstanceLookup = ThreadSafe::Cache.new
474
+ InstanceLookup = ::Concurrent::Map.new
431
475
 
432
476
  READ_BUFFER = 2048
433
477
 
@@ -459,13 +503,47 @@ keystr
459
503
  SSL.SSL_set_verify(@ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, VerifyCB)
460
504
  end
461
505
 
506
+ # Add Server Name Indication (SNI) for client connections
507
+ if options[:host_name]
508
+ if server
509
+ @hosts = ::Concurrent::Map.new
510
+ @hosts[options[:host_name].to_s] = @context
511
+ @context.add_server_name_indication
512
+ else
513
+ SSL.SSL_set_tlsext_host_name(@ssl, options[:host_name])
514
+ end
515
+ end
516
+
462
517
  SSL.SSL_connect(@ssl) unless server
463
518
  end
464
519
 
465
520
 
466
- attr_reader :is_server
467
- attr_reader :context
521
+ def add_host(host_name:, **options)
522
+ raise 'Server Name Indication (SNI) not configured for default host' unless @hosts
523
+ raise 'only valid for server mode context' unless @is_server
524
+ context = Context.new(true, options)
525
+ @hosts[host_name.to_s] = context
526
+ context.add_server_name_indication
527
+ nil
528
+ end
529
+
530
+ # Careful with this.
531
+ # If you remove all the hosts you'll end up with a segfault
532
+ def remove_host(host_name)
533
+ raise 'Server Name Indication (SNI) not configured for default host' unless @hosts
534
+ raise 'only valid for server mode context' unless @is_server
535
+ context = @hosts[host_name.to_s]
536
+ if context
537
+ @hosts.delete(host_name.to_s)
538
+ context.cleanup
539
+ end
540
+ nil
541
+ end
542
+
543
+
544
+ attr_reader :is_server, :context
468
545
  attr_reader :handshake_completed
546
+ attr_reader :hosts
469
547
 
470
548
 
471
549
  def get_peer_cert
@@ -582,7 +660,14 @@ keystr
582
660
 
583
661
  SSL.SSL_free @ssl
584
662
 
585
- @context.cleanup
663
+ if @hosts
664
+ @hosts.each_value do |context|
665
+ context.cleanup
666
+ end
667
+ @hosts = nil
668
+ else
669
+ @context.cleanup
670
+ end
586
671
  end
587
672
 
588
673
  # Called from class level callback function
@@ -677,7 +762,7 @@ keystr
677
762
  end
678
763
 
679
764
 
680
- CIPHER_DISPATCH_FAILED = 'Cipher text dispatch failed'.freeze
765
+ CIPHER_DISPATCH_FAILED = 'Cipher text dispatch failed'
681
766
  def dispatch_cipher_text
682
767
  begin
683
768
  did_work = false
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RubyTls
2
- VERSION = "2.1.2"
4
+ VERSION = '2.3.0'
3
5
  end
data/ruby-tls.gemspec CHANGED
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  # -*- encoding: utf-8 -*-
3
+
2
4
  $:.push File.expand_path("../lib", __FILE__)
3
5
  require "ruby-tls/version"
4
6
 
@@ -11,15 +13,15 @@ Gem::Specification.new do |s|
11
13
  s.homepage = "https://github.com/cotag/ruby-tls"
12
14
  s.summary = "Abstract TLS for Ruby"
13
15
  s.description = <<-EOF
14
- Allows transport layers outside Ruby TCP be secured.
16
+ Allows transport layers outside Ruby TCP to be secured.
15
17
  EOF
16
18
 
17
19
 
18
- s.add_dependency 'ffi-compiler', '>= 0.0.2'
19
- s.add_dependency 'thread_safe'
20
+ s.add_dependency 'ffi-compiler', '>= 1.0', '< 2.0'
21
+ s.add_dependency 'concurrent-ruby', '~> 1.0'
20
22
 
21
- s.add_development_dependency 'rspec'
22
- s.add_development_dependency 'yard'
23
+ s.add_development_dependency 'rspec', '~> 3.5'
24
+ s.add_development_dependency 'yard', '~> 0.9'
23
25
 
24
26
 
25
27
  s.files = Dir["{lib}/**/*"] + %w(ruby-tls.gemspec README.md)
data/spec/verify_spec.rb CHANGED
@@ -6,7 +6,7 @@ describe RubyTls do
6
6
  class Client2
7
7
  def initialize(client_data, dir)
8
8
  @client_data = client_data
9
- @ssl = RubyTls::SSL::Box.new(false, self, private_key: dir + 'client.key', cert_chain: dir + 'client.crt')
9
+ @ssl = RubyTls::SSL::Box.new(false, self, private_key: dir + 'client.key', cert_chain: dir + 'client.crt', host_name: 'just.testing.com')
10
10
  end
11
11
 
12
12
  attr_reader :ssl
@@ -97,6 +97,118 @@ describe RubyTls do
97
97
  end
98
98
 
99
99
 
100
+ it "should verify the hostname" do
101
+ @server_data = []
102
+ @client_data = []
103
+
104
+ class Server3
105
+ def initialize(client, server_data)
106
+ @client = client
107
+ @server_data = server_data
108
+ @ssl = RubyTls::SSL::Box.new(true, self, host_name: 'just.testing.com')
109
+ end
110
+
111
+ attr_reader :ssl
112
+ attr_accessor :started
113
+ attr_accessor :stop
114
+ attr_accessor :cert_from_server
115
+
116
+ def close_cb
117
+ @server_data << 'close'
118
+ @stop = true
119
+ end
120
+
121
+ def dispatch_cb(data)
122
+ @server_data << data
123
+ end
124
+
125
+ def transmit_cb(data)
126
+ @client.ssl.decrypt(data) unless @stop
127
+ end
128
+
129
+ def handshake_cb(protocol)
130
+ @server_data << 'ready'
131
+ end
132
+ end
133
+
134
+
135
+ @client = Client2.new(@client_data, @dir)
136
+ @server = Server3.new(@client, @server_data)
137
+ @client.server = @server
138
+
139
+ @client.ssl.start
140
+ @client.ssl.cleanup
141
+ @server.ssl.cleanup
142
+
143
+ expect(@client_data).to eq(['ready'])
144
+ expect(@server_data).to eq(['ready'])
145
+ end
146
+
147
+ it "should fail if host name not found" do
148
+ @server_data = []
149
+ @client_data = []
150
+
151
+ class Server4
152
+ def initialize(client, server_data)
153
+ @client = client
154
+ @server_data = server_data
155
+ @ssl = RubyTls::SSL::Box.new(true, self, host_name: 'testing.com')
156
+ end
157
+
158
+ attr_reader :ssl
159
+ attr_accessor :started
160
+ attr_accessor :stop
161
+ attr_accessor :cert_from_server
162
+
163
+ def close_cb
164
+ @server_data << 'close'
165
+ @stop = true
166
+ end
167
+
168
+ def dispatch_cb(data)
169
+ @server_data << data
170
+ end
171
+
172
+ def transmit_cb(data)
173
+ @client.ssl.decrypt(data) unless @stop
174
+ end
175
+
176
+ def handshake_cb(protocol)
177
+ @server_data << 'ready'
178
+ end
179
+ end
180
+
181
+
182
+ @client = Client2.new(@client_data, @dir)
183
+ @server = Server4.new(@client, @server_data)
184
+ @client.server = @server
185
+
186
+ @client.ssl.start
187
+ @client.ssl.cleanup
188
+ @server.ssl.cleanup
189
+
190
+ expect(@client_data).to eq([])
191
+ expect(@server_data).to eq(['close'])
192
+ end
193
+
194
+ it "test actually adding a second context" do
195
+ @server_data = []
196
+ @client_data = []
197
+
198
+ @client = Client2.new(@client_data, @dir)
199
+ @server = Server4.new(@client, @server_data)
200
+ @client.server = @server
201
+ @server.ssl.add_host host_name: 'just.testing.com'
202
+
203
+ @client.ssl.start
204
+ @client.ssl.cleanup
205
+ @server.ssl.cleanup
206
+
207
+ expect(@client_data).to eq(['ready'])
208
+ expect(@server_data).to eq(['ready'])
209
+ end
210
+
211
+
100
212
  it "should deny the connection" do
101
213
  @server_data = []
102
214
  @client_data = []
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-tls
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.2
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen von Takach
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-19 00:00:00.000000000 Z
11
+ date: 2017-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi-compiler
@@ -16,57 +16,63 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.2
19
+ version: '1.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2.0'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: 0.0.2
29
+ version: '1.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
27
33
  - !ruby/object:Gem::Dependency
28
- name: thread_safe
34
+ name: concurrent-ruby
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
- - - ">="
37
+ - - "~>"
32
38
  - !ruby/object:Gem::Version
33
- version: '0'
39
+ version: '1.0'
34
40
  type: :runtime
35
41
  prerelease: false
36
42
  version_requirements: !ruby/object:Gem::Requirement
37
43
  requirements:
38
- - - ">="
44
+ - - "~>"
39
45
  - !ruby/object:Gem::Version
40
- version: '0'
46
+ version: '1.0'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: rspec
43
49
  requirement: !ruby/object:Gem::Requirement
44
50
  requirements:
45
- - - ">="
51
+ - - "~>"
46
52
  - !ruby/object:Gem::Version
47
- version: '0'
53
+ version: '3.5'
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
- - - ">="
58
+ - - "~>"
53
59
  - !ruby/object:Gem::Version
54
- version: '0'
60
+ version: '3.5'
55
61
  - !ruby/object:Gem::Dependency
56
62
  name: yard
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
- - - ">="
65
+ - - "~>"
60
66
  - !ruby/object:Gem::Version
61
- version: '0'
67
+ version: '0.9'
62
68
  type: :development
63
69
  prerelease: false
64
70
  version_requirements: !ruby/object:Gem::Requirement
65
71
  requirements:
66
- - - ">="
72
+ - - "~>"
67
73
  - !ruby/object:Gem::Version
68
- version: '0'
69
- description: " Allows transport layers outside Ruby TCP be secured.\n"
74
+ version: '0.9'
75
+ description: " Allows transport layers outside Ruby TCP to be secured.\n"
70
76
  email:
71
77
  - steve@cotag.me
72
78
  executables: []
@@ -114,4 +120,3 @@ test_files:
114
120
  - spec/client.key
115
121
  - spec/comms_spec.rb
116
122
  - spec/verify_spec.rb
117
- has_rdoc: