Skip to content

Commit 2ccda32

Browse files
committed
[compat] align with Ruby OpenSSL 2.2.0
1 parent 4f3894d commit 2ccda32

File tree

14 files changed

+545
-111
lines changed

14 files changed

+545
-111
lines changed

lib/jopenssl23/openssl.rb

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# frozen_string_literal: false
1+
# frozen_string_literal: true
22
=begin
33
= Info
44
'OpenSSL for Ruby 2' project
@@ -10,10 +10,26 @@
1010
(See the file 'LICENCE'.)
1111
=end
1212

13-
require 'openssl/bn'
14-
require 'openssl/pkey'
15-
require 'openssl/cipher'
16-
require 'openssl/config' if OpenSSL.const_defined?(:Config, false)
17-
require 'openssl/digest'
18-
require 'openssl/x509'
19-
require 'openssl/ssl'
13+
require_relative 'openssl/bn'
14+
require_relative 'openssl/pkey'
15+
require_relative 'openssl/cipher'
16+
require_relative 'openssl/config' if OpenSSL.const_defined?(:Config, false)
17+
require_relative 'openssl/digest'
18+
require_relative 'openssl/hmac'
19+
require_relative 'openssl/x509'
20+
require_relative 'openssl/ssl'
21+
require_relative 'openssl/pkcs5'
22+
23+
module OpenSSL
24+
# call-seq:
25+
# OpenSSL.secure_compare(string, string) -> boolean
26+
#
27+
# Constant time memory comparison. Inputs are hashed using SHA-256 to mask
28+
# the length of the secret. Returns +true+ if the strings are identical,
29+
# +false+ otherwise.
30+
def self.secure_compare(a, b)
31+
hashed_a = OpenSSL::Digest.digest('SHA256', a)
32+
hashed_b = OpenSSL::Digest.digest('SHA256', b)
33+
OpenSSL.fixed_length_secure_compare(hashed_a, hashed_b) && a == b
34+
end
35+
end

lib/jopenssl23/openssl/bn.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# frozen_string_literal: false
1+
# frozen_string_literal: true
22
#--
33
#
44
# = Ruby-space definitions that completes C-space funcs for BN
@@ -15,6 +15,8 @@
1515

1616
module OpenSSL
1717
class BN
18+
include Comparable
19+
1820
def pretty_print(q)
1921
q.object_group(self) {
2022
q.text ' '

lib/jopenssl23/openssl/buffering.rb

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# coding: binary
2-
# frozen_string_literal: false
2+
# frozen_string_literal: true
33
#--
44
#= Info
55
# 'OpenSSL for Ruby 2' project
@@ -22,6 +22,29 @@
2222
module OpenSSL::Buffering
2323
include Enumerable
2424

25+
# A buffer which will retain binary encoding.
26+
class Buffer < String
27+
BINARY = Encoding::BINARY
28+
29+
def initialize
30+
super
31+
32+
force_encoding(BINARY)
33+
end
34+
35+
def << string
36+
if string.encoding == BINARY
37+
super(string)
38+
else
39+
super(string.b)
40+
end
41+
42+
return self
43+
end
44+
45+
alias concat <<
46+
end
47+
2548
##
2649
# The "sync mode" of the SSLSocket.
2750
#
@@ -40,7 +63,7 @@ module OpenSSL::Buffering
4063
def initialize(*)
4164
# super
4265
@eof = false
43-
@rbuffer = ""
66+
@rbuffer = Buffer.new
4467
@sync = @io.sync
4568
end
4669

@@ -312,7 +335,7 @@ def eof?
312335
# buffer is flushed to the underlying socket.
313336

314337
def do_write(s)
315-
@wbuffer = "" unless defined? @wbuffer
338+
@wbuffer = Buffer.new unless defined? @wbuffer
316339
@wbuffer << s
317340
@wbuffer.force_encoding(Encoding::BINARY)
318341
@sync ||= false
@@ -398,7 +421,7 @@ def <<(s)
398421
# See IO#puts for full details.
399422

400423
def puts(*args)
401-
s = ""
424+
s = Buffer.new
402425
if args.empty?
403426
s << "\n"
404427
end
@@ -416,7 +439,7 @@ def puts(*args)
416439
# See IO#print for full details.
417440

418441
def print(*args)
419-
s = ""
442+
s = Buffer.new
420443
args.each{ |arg| s << arg.to_s }
421444
do_write(s)
422445
nil

lib/jopenssl23/openssl/cipher.rb

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# frozen_string_literal: false
1+
# frozen_string_literal: true
22
#--
33
# = Ruby-space predefined Cipher subclasses
44
#
@@ -14,6 +14,48 @@
1414

1515
module OpenSSL
1616
class Cipher
17+
# %w(AES CAST5 BF DES IDEA RC2 RC4 RC5).each{|name|
18+
# klass = Class.new(Cipher){
19+
# define_method(:initialize){|*args|
20+
# cipher_name = args.inject(name){|n, arg| "#{n}-#{arg}" }
21+
# super(cipher_name.downcase)
22+
# }
23+
# }
24+
# const_set(name, klass)
25+
# }
26+
#
27+
# %w(128 192 256).each{|keylen|
28+
# klass = Class.new(Cipher){
29+
# define_method(:initialize){|mode = "CBC"|
30+
# super("aes-#{keylen}-#{mode}".downcase)
31+
# }
32+
# }
33+
# const_set("AES#{keylen}", klass)
34+
# }
35+
36+
# call-seq:
37+
# cipher.random_key -> key
38+
#
39+
# Generate a random key with OpenSSL::Random.random_bytes and sets it to
40+
# the cipher, and returns it.
41+
#
42+
# You must call #encrypt or #decrypt before calling this method.
43+
# def random_key
44+
# str = OpenSSL::Random.random_bytes(self.key_len)
45+
# self.key = str
46+
# end
47+
48+
# call-seq:
49+
# cipher.random_iv -> iv
50+
#
51+
# Generate a random IV with OpenSSL::Random.random_bytes and sets it to the
52+
# cipher, and returns it.
53+
#
54+
# You must call #encrypt or #decrypt before calling this method.
55+
# def random_iv
56+
# str = OpenSSL::Random.random_bytes(self.iv_len)
57+
# self.iv = str
58+
# end
1759

1860
# Deprecated.
1961
#

lib/jopenssl23/openssl/config.rb

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# frozen_string_literal: false
1+
# frozen_string_literal: true
22
=begin
33
= Ruby-space definitions that completes C-space funcs for Config
44
@@ -37,7 +37,7 @@ class << self
3737
def parse(string)
3838
c = new()
3939
parse_config(StringIO.new(string)).each do |section, hash|
40-
c[section] = hash
40+
c.set_section(section, hash)
4141
end
4242
c
4343
end
@@ -53,9 +53,8 @@ def parse(string)
5353
def parse_config(io)
5454
begin
5555
parse_config_lines(io)
56-
rescue ConfigError => e
57-
e.message.replace("error in line #{io.lineno}: " + e.message)
58-
raise
56+
rescue => error
57+
raise ConfigError, "error in line #{io.lineno}: " + error.message
5958
end
6059
end
6160

@@ -77,29 +76,44 @@ def get_key_string(data, section, key) # :nodoc:
7776
def parse_config_lines(io)
7877
section = 'default'
7978
data = {section => {}}
80-
while definition = get_definition(io)
79+
io_stack = [io]
80+
while definition = get_definition(io_stack)
8181
definition = clear_comments(definition)
8282
next if definition.empty?
83-
if definition[0] == ?[
83+
case definition
84+
when /\A\[/
8485
if /\[([^\]]*)\]/ =~ definition
8586
section = $1.strip
8687
data[section] ||= {}
8788
else
8889
raise ConfigError, "missing close square bracket"
8990
end
90-
else
91-
if /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/ =~ definition
92-
if $2
93-
section = $1
94-
key = $2
95-
else
96-
key = $1
91+
when /\A\.include (\s*=\s*)?(.+)\z/
92+
path = $2
93+
if File.directory?(path)
94+
files = Dir.glob(File.join(path, "*.{cnf,conf}"), File::FNM_EXTGLOB)
95+
else
96+
files = [path]
97+
end
98+
99+
files.each do |filename|
100+
begin
101+
io_stack << StringIO.new(File.read(filename))
102+
rescue
103+
raise ConfigError, "could not include file '%s'" % filename
97104
end
98-
value = unescape_value(data, section, $3)
99-
(data[section] ||= {})[key] = value.strip
105+
end
106+
when /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/
107+
if $2
108+
section = $1
109+
key = $2
100110
else
101-
raise ConfigError, "missing equal sign"
111+
key = $1
102112
end
113+
value = unescape_value(data, section, $3)
114+
(data[section] ||= {})[key] = value.strip
115+
else
116+
raise ConfigError, "missing equal sign"
103117
end
104118
end
105119
data
@@ -212,10 +226,10 @@ def clear_comments(line)
212226
scanned.join
213227
end
214228

215-
def get_definition(io)
216-
if line = get_line(io)
229+
def get_definition(io_stack)
230+
if line = get_line(io_stack)
217231
while /[^\\]\\\z/ =~ line
218-
if extra = get_line(io)
232+
if extra = get_line(io_stack)
219233
line += extra
220234
else
221235
break
@@ -225,9 +239,12 @@ def get_definition(io)
225239
end
226240
end
227241

228-
def get_line(io)
229-
if line = io.gets
230-
line.gsub(/[\r\n]*/, '')
242+
def get_line(io_stack)
243+
while io = io_stack.last
244+
if line = io.gets
245+
return line.gsub(/[\r\n]*/, '')
246+
end
247+
io_stack.pop
231248
end
232249
end
233250
end
@@ -249,7 +266,7 @@ def initialize(filename = nil)
249266
if filename
250267
File.open(filename.to_s) do |file|
251268
Config.parse_config(file).each do |section, hash|
252-
self[section] = hash
269+
set_section(section, hash)
253270
end
254271
end
255272
end
@@ -298,6 +315,8 @@ def value(arg1, arg2 = nil) # :nodoc:
298315
end
299316

300317
##
318+
# *Deprecated in v2.2.0*. This method will be removed in a future release.
319+
#
301320
# Set the target _key_ with a given _value_ under a specific _section_.
302321
#
303322
# Given the following configurating file being loaded:
@@ -352,6 +371,8 @@ def section(name) # :nodoc:
352371
end
353372

354373
##
374+
# *Deprecated in v2.2.0*. This method will be removed in a future release.
375+
#
355376
# Sets a specific _section_ name with a Hash _pairs_.
356377
#
357378
# Given the following configuration being created:
@@ -377,9 +398,13 @@ def section(name) # :nodoc:
377398
#
378399
def []=(section, pairs)
379400
check_modify
380-
@data[section] ||= {}
401+
set_section(section, pairs)
402+
end
403+
404+
def set_section(section, pairs) # :nodoc:
405+
hash = @data[section] ||= {}
381406
pairs.each do |key, value|
382-
self.add_value(section, key, value)
407+
hash[key] = value
383408
end
384409
end
385410

@@ -464,6 +489,8 @@ def initialize_copy(other)
464489
end
465490

466491
def check_modify
492+
warn "#{caller(2, 1)[0]}: warning: do not modify OpenSSL::Config; this " \
493+
"method is deprecated and will be removed in a future release."
467494
raise TypeError.new("Insecure: can't modify OpenSSL config") if frozen?
468495
end
469496

lib/jopenssl23/openssl/digest.rb

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# frozen_string_literal: false
1+
# frozen_string_literal: true
22
#--
33
# = Ruby-space predefined Digest subclasses
44
#
@@ -15,6 +15,36 @@
1515
module OpenSSL
1616
class Digest
1717

18+
# Return the hash value computed with _name_ Digest. _name_ is either the
19+
# long name or short name of a supported digest algorithm.
20+
#
21+
# === Examples
22+
#
23+
# OpenSSL::Digest.digest("SHA256", "abc")
24+
#
25+
# which is equivalent to:
26+
#
27+
# OpenSSL::Digest.digest('SHA256', "abc")
28+
#
29+
# def self.digest(name, data)
30+
# super(data, name)
31+
# end
32+
#
33+
# %w(MD4 MD5 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512).each do |name|
34+
# klass = Class.new(self) {
35+
# define_method(:initialize, ->(data = nil) {super(name, data)})
36+
# }
37+
#
38+
# singleton = (class << klass; self; end)
39+
#
40+
# singleton.class_eval{
41+
# define_method(:digest) {|data| new.digest(data)}
42+
# define_method(:hexdigest) {|data| new.hexdigest(data)}
43+
# }
44+
#
45+
# const_set(name.tr('-', '_'), klass)
46+
# end
47+
1848
# Deprecated.
1949
#
2050
# This class is only provided for backwards compatibility.

0 commit comments

Comments
 (0)