ruby-tls 2.1.2 → 2.3.0

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 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: