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 +4 -4
- data/lib/ruby-tls.rb +2 -1
- data/lib/ruby-tls/ssl.rb +103 -18
- data/lib/ruby-tls/version.rb +3 -1
- data/ruby-tls.gemspec +7 -5
- data/spec/verify_spec.rb +113 -1
- metadata +24 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b85f5f85aa66d137ff1ff2bd3894adaf81d07679
|
4
|
+
data.tar.gz: e0034106218aaa3d0ecc11ff067363a364eababa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4b103bd35d4eeb0f6e88ec95d03cdde3d282256567ac8ae6b6e2a714b8d81d16cc049475499bdca85aa4971ecfdc1f43caeafb73ef5a03509e3f93f125ac813
|
7
|
+
data.tar.gz: 5b50ebc3041e80fdba1fb24bcb55e3a3fc013e22e6ce23b5bb873badff8fa23b79c2e65818955a893081963367da21bf6c266159e02300da04dc1d254d209833
|
data/lib/ruby-tls.rb
CHANGED
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 '
|
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'
|
293
|
-
SESSION = 'ruby-tls'
|
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 =
|
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 =
|
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 =
|
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
|
-
|
467
|
-
|
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
|
-
@
|
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'
|
765
|
+
CIPHER_DISPATCH_FAILED = 'Cipher text dispatch failed'
|
681
766
|
def dispatch_cipher_text
|
682
767
|
begin
|
683
768
|
did_work = false
|
data/lib/ruby-tls/version.rb
CHANGED
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',
|
19
|
-
s.add_dependency '
|
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.
|
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:
|
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:
|
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:
|
29
|
+
version: '1.0'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.0'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
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: '
|
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: '
|
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:
|