jruby-openssl 0.16.2-java → 0.19.0-java
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/History.md +104 -0
- data/Mavenfile +180 -93
- data/README.md +100 -27
- data/Rakefile +77 -26
- data/lib/jopenssl/load.rb +13 -57
- data/lib/jopenssl/version.rb +2 -7
- data/lib/jopenssl.jar +0 -0
- data/lib/openssl/config.rb +13 -5
- data/lib/openssl/ssl.rb +21 -11
- data/lib/openssl.rb +43 -0
- data/pom.xml +656 -108
- metadata +20 -21
- data/lib/openssl/pkcs12.rb +0 -101
- /data/{lib → vendor}/org/bouncycastle/bcpkix-jdk18on/1.85/bcpkix-jdk18on-1.85.jar +0 -0
- /data/{lib → vendor}/org/bouncycastle/bcprov-jdk18on/1.85/bcprov-jdk18on-1.85.jar +0 -0
- /data/{lib → vendor}/org/bouncycastle/bctls-jdk18on/1.85/bctls-jdk18on-1.85.jar +0 -0
- /data/{lib → vendor}/org/bouncycastle/bcutil-jdk18on/1.85/bcutil-jdk18on-1.85.jar +0 -0
data/Rakefile
CHANGED
|
@@ -1,57 +1,108 @@
|
|
|
1
|
-
|
|
1
|
+
require 'rake/testtask'
|
|
2
|
+
|
|
3
|
+
mvnw = File.expand_path('./mvnw', File.dirname(__FILE__))
|
|
4
|
+
|
|
5
|
+
# reproducible build: SOURCE_DATE_EPOCH shared by jopenssl.jar and gem
|
|
6
|
+
# read from env by RubyGems; pass via -D so dev builds don't churn pom.xml
|
|
7
|
+
def source_date_epoch!
|
|
8
|
+
ENV['SOURCE_DATE_EPOCH'] ||= `git log -1 --pretty=%ct`.strip
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def _build_output_timestamp
|
|
12
|
+
"-Dproject.build.outputTimestamp=#{ENV['SOURCE_DATE_EPOCH']}" if ENV['SOURCE_DATE_EPOCH']
|
|
13
|
+
end
|
|
2
14
|
|
|
3
|
-
#Rake::Task[:jar].clear rescue nil
|
|
4
15
|
desc "Package jopenssl.jar with the compiled classes"
|
|
5
16
|
task :jar do
|
|
6
|
-
sh(
|
|
17
|
+
sh("#{mvnw} prepare-package -Dmaven.test.skip=true")
|
|
7
18
|
end
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
sh( './mvnw package -Dmaven.test.skip=true' )
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
task :test_prepare do
|
|
15
|
-
sh( './mvnw prepare-package -Dmaven.test.skip=true' )
|
|
16
|
-
sh( './mvnw test-compile' ) # separate step due -Dmaven.test.skip=true
|
|
19
|
+
|
|
20
|
+
task :test_prepare => :jar do
|
|
21
|
+
sh("#{mvnw} test-compile") # separate due -Dmaven.test.skip=true
|
|
17
22
|
end
|
|
18
23
|
|
|
19
24
|
task :clean do
|
|
20
|
-
sh(
|
|
25
|
+
sh("#{mvnw} clean")
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
task :build do
|
|
24
|
-
|
|
29
|
+
source_date_epoch!
|
|
30
|
+
sh("#{mvnw} -Prelease -DupdateReleaseInfo=true #{_build_output_timestamp} clean package")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
desc "Sanity-check the tree/version, then build a reproducible release gem"
|
|
34
|
+
task :release => :release_check do
|
|
35
|
+
source_date_epoch! # pin to the release commit so the gem + jar use the same
|
|
36
|
+
puts "SOURCE_DATE_EPOCH=#{ENV['SOURCE_DATE_EPOCH']} (#{Time.at(ENV['SOURCE_DATE_EPOCH'].to_i).utc})"
|
|
37
|
+
Rake::Task[:build].invoke
|
|
38
|
+
gem = Dir['target/*.gem', 'pkg/*.gem'].max_by { |f| File.mtime(f) }
|
|
39
|
+
abort "release aborted - no .gem produced" unless gem
|
|
40
|
+
puts "built #{gem}"
|
|
41
|
+
# gem push #{gem} (or: #{File.basename(mvnw)} deploy -Pjar-release for the jar artifact)"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
task :release_check do
|
|
45
|
+
dirty = `git status --porcelain`.strip
|
|
46
|
+
abort "release aborted - working tree is not clean:\n#{dirty}" unless dirty.empty?
|
|
47
|
+
|
|
48
|
+
load File.expand_path('lib/jopenssl/version.rb', File.dirname(__FILE__))
|
|
49
|
+
version = JOpenSSL::VERSION
|
|
50
|
+
|
|
51
|
+
if Gem::Version.new(version).prerelease? && ENV['PRERELEASE'] != 'true'
|
|
52
|
+
abort "release aborted - #{version} is a prerelease"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
branch = `git rev-parse --abbrev-ref HEAD`.strip
|
|
56
|
+
warn "WARNING: releasing from '#{branch}' (not 'master')" unless branch == 'master'
|
|
57
|
+
|
|
58
|
+
tag = `git tag --points-at HEAD`.split("\n").find { |t| t =~ /#{Regexp.escape(version)}/ }
|
|
59
|
+
warn "WARNING: no git tag matching #{version} points at HEAD" unless tag
|
|
60
|
+
|
|
61
|
+
puts "release checks passed for #{version}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
desc "Build the self-contained jar-release artifacts (-Pjar-release)"
|
|
65
|
+
task :jar_release => :release_check do
|
|
66
|
+
source_date_epoch!
|
|
67
|
+
puts "SOURCE_DATE_EPOCH=#{ENV['SOURCE_DATE_EPOCH']} (#{Time.at(ENV['SOURCE_DATE_EPOCH'].to_i).utc})"
|
|
68
|
+
sh("#{mvnw} package -Pjar-release -Dmaven.test.skip=true #{_build_output_timestamp}")
|
|
69
|
+
# deploy (gpg-sign + push): #{File.basename(mvnw)} deploy -Prelease,jar-release -D...
|
|
25
70
|
end
|
|
26
71
|
|
|
27
72
|
task :default => :build
|
|
28
73
|
|
|
29
|
-
file('lib/jopenssl.jar') { Rake::Task[
|
|
74
|
+
file('lib/jopenssl.jar') { Rake::Task[:jar].invoke }
|
|
75
|
+
|
|
76
|
+
file('pkg/test-classes/org/jruby/ext/openssl/SecurityHelperTest.class') do
|
|
77
|
+
Rake::Task[:test_prepare].invoke
|
|
78
|
+
end
|
|
30
79
|
|
|
31
|
-
require 'rake/testtask'
|
|
32
80
|
Rake::TestTask.new do |task|
|
|
33
|
-
task.libs << File.expand_path('
|
|
34
|
-
test_files = FileList['
|
|
35
|
-
task.test_files = test_files.map { |path| path.sub('
|
|
81
|
+
task.libs << File.expand_path('test', File.dirname(__FILE__))
|
|
82
|
+
test_files = FileList['test/**/test*.rb'].to_a
|
|
83
|
+
task.test_files = test_files.map { |path| path.sub('test/', '') }
|
|
36
84
|
task.verbose = false # using -v directly instead due issues with rake
|
|
37
85
|
task.loader = :direct
|
|
38
|
-
task.ruby_opts = [ '-v', '-
|
|
86
|
+
task.ruby_opts = [ '-v', '-rbundler/setup' ]
|
|
39
87
|
end
|
|
40
|
-
task :test => 'lib/jopenssl.jar'
|
|
88
|
+
task :test => ['lib/jopenssl.jar', 'pkg/test-classes/org/jruby/ext/openssl/SecurityHelperTest.class']
|
|
89
|
+
|
|
90
|
+
require_relative 'tasks/vendor_tests'
|
|
91
|
+
define_vendor_test_tasks # root + jopenssl_lib default to this tree
|
|
41
92
|
|
|
42
93
|
namespace :integration do
|
|
43
|
-
it_path = File.expand_path('
|
|
94
|
+
it_path = File.expand_path('integration', File.dirname(__FILE__))
|
|
44
95
|
task :install do
|
|
45
96
|
ruby "-C #{it_path} -S bundle install"
|
|
46
97
|
end
|
|
47
|
-
|
|
98
|
+
desc "Run tests via invoker (bc-compat)"
|
|
48
99
|
task :test => 'lib/jopenssl.jar' do
|
|
49
100
|
unless File.exist?(File.join(it_path, 'Gemfile.lock'))
|
|
50
|
-
|
|
101
|
+
fail "bundle not installed, run `rake integration:install'"
|
|
51
102
|
end
|
|
52
103
|
loader = "ARGV.each { |file| require(file) }"
|
|
53
104
|
lib = [ File.expand_path('../lib', __FILE__), it_path ]
|
|
54
|
-
test_files = FileList['
|
|
55
|
-
ruby "-I#{lib.join(':')} -C
|
|
105
|
+
test_files = FileList['integration/*_test.rb'].map { |path| path.sub('integration/', '') }
|
|
106
|
+
ruby "-I#{lib.join(':')} -C integration -e \"#{loader}\" #{test_files.map { |f| "\"#{f}\"" }.join(' ')}"
|
|
56
107
|
end
|
|
57
108
|
end
|
data/lib/jopenssl/load.rb
CHANGED
|
@@ -1,71 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
require_relative 'version'
|
|
2
2
|
|
|
3
|
-
# NOTE: assuming user does pull in BC .jars from somewhere else on the
|
|
4
|
-
|
|
3
|
+
# NOTE: assuming user does pull in BC .jars from somewhere else on the class-path
|
|
4
|
+
if ENV_JAVA['jruby.openssl.load.jars'] != 'false' &&
|
|
5
|
+
java.security.Security.getProvider('BC').nil?
|
|
5
6
|
version = JOpenSSL::BOUNCY_CASTLE_VERSION
|
|
6
|
-
begin
|
|
7
|
-
require '
|
|
8
|
-
# if we have jar-dependencies we let it track the jars
|
|
7
|
+
bc_jars = begin
|
|
8
|
+
require 'jar_dependencies' unless respond_to?(:require_jar, true)
|
|
9
9
|
require_jar 'org.bouncycastle', 'bcprov-jdk18on', version
|
|
10
10
|
require_jar 'org.bouncycastle', 'bcpkix-jdk18on', version
|
|
11
11
|
require_jar 'org.bouncycastle', 'bcutil-jdk18on', version
|
|
12
12
|
require_jar 'org.bouncycastle', 'bctls-jdk18on', version
|
|
13
|
-
|
|
13
|
+
true
|
|
14
14
|
rescue LoadError, RuntimeError
|
|
15
|
-
|
|
15
|
+
false
|
|
16
16
|
end
|
|
17
17
|
unless bc_jars
|
|
18
|
-
|
|
19
|
-
load "org/bouncycastle/
|
|
20
|
-
load "org/bouncycastle/
|
|
21
|
-
load "org/bouncycastle/
|
|
18
|
+
vendor = File.expand_path('../../vendor', __dir__)
|
|
19
|
+
load "#{vendor}/org/bouncycastle/bcprov-jdk18on/#{version}/bcprov-jdk18on-#{version}.jar"
|
|
20
|
+
load "#{vendor}/org/bouncycastle/bcpkix-jdk18on/#{version}/bcpkix-jdk18on-#{version}.jar"
|
|
21
|
+
load "#{vendor}/org/bouncycastle/bcutil-jdk18on/#{version}/bcutil-jdk18on-#{version}.jar"
|
|
22
|
+
load "#{vendor}/org/bouncycastle/bctls-jdk18on/#{version}/bctls-jdk18on-#{version}.jar"
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
require 'jopenssl.jar'
|
|
26
27
|
JRuby::Util.load_ext('org.jruby.ext.openssl.OpenSSL')
|
|
27
|
-
|
|
28
|
-
# NOTE: content bellow should live in *lib/openssl.rb* but due RubyGems/Bundler
|
|
29
|
-
# `autoload :OpenSSL` this will cause issues if an older version (0.11) is the
|
|
30
|
-
# default gem under JRuby 9.2 (which on auto-load does not trigger a dynamic
|
|
31
|
-
# require - this is only fixed in JRuby 9.3)
|
|
32
|
-
|
|
33
|
-
module OpenSSL
|
|
34
|
-
autoload :Config, 'openssl/config' unless const_defined?(:Config, false)
|
|
35
|
-
autoload :ConfigError, 'openssl/config' unless const_defined?(:ConfigError, false)
|
|
36
|
-
autoload :PKCS12, 'openssl/pkcs12'
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
=begin
|
|
40
|
-
= Info
|
|
41
|
-
'OpenSSL for Ruby 2' project
|
|
42
|
-
Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
|
|
43
|
-
All rights reserved.
|
|
44
|
-
|
|
45
|
-
= Licence
|
|
46
|
-
This program is licensed under the same licence as Ruby.
|
|
47
|
-
(See the file 'LICENCE'.)
|
|
48
|
-
=end
|
|
49
|
-
|
|
50
|
-
require 'openssl/bn'
|
|
51
|
-
require 'openssl/pkey'
|
|
52
|
-
require 'openssl/cipher'
|
|
53
|
-
require 'openssl/digest'
|
|
54
|
-
require 'openssl/hmac'
|
|
55
|
-
require 'openssl/x509'
|
|
56
|
-
require 'openssl/ssl'
|
|
57
|
-
require 'openssl/pkcs5'
|
|
58
|
-
|
|
59
|
-
module OpenSSL
|
|
60
|
-
# call-seq:
|
|
61
|
-
# OpenSSL.secure_compare(string, string) -> boolean
|
|
62
|
-
#
|
|
63
|
-
# Constant time memory comparison. Inputs are hashed using SHA-256 to mask
|
|
64
|
-
# the length of the secret. Returns +true+ if the strings are identical,
|
|
65
|
-
# +false+ otherwise.
|
|
66
|
-
def self.secure_compare(a, b)
|
|
67
|
-
hashed_a = OpenSSL::Digest.digest('SHA256', a)
|
|
68
|
-
hashed_b = OpenSSL::Digest.digest('SHA256', b)
|
|
69
|
-
OpenSSL.fixed_length_secure_compare(hashed_a, hashed_b) && a == b
|
|
70
|
-
end
|
|
71
|
-
end
|
data/lib/jopenssl/version.rb
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module JOpenSSL
|
|
2
|
-
VERSION = '0.
|
|
3
|
+
VERSION = '0.19.0'
|
|
3
4
|
BOUNCY_CASTLE_VERSION = '1.85'
|
|
4
5
|
end
|
|
5
|
-
|
|
6
|
-
Object.class_eval do
|
|
7
|
-
Jopenssl = JOpenSSL
|
|
8
|
-
private_constant :Jopenssl if respond_to?(:private_constant)
|
|
9
|
-
deprecate_constant :Jopenssl if respond_to?(:deprecate_constant)
|
|
10
|
-
end
|
data/lib/jopenssl.jar
CHANGED
|
Binary file
|
data/lib/openssl/config.rb
CHANGED
|
@@ -80,7 +80,9 @@ module OpenSSL
|
|
|
80
80
|
section = 'default'
|
|
81
81
|
data = {section => {}}
|
|
82
82
|
io_stack = [io]
|
|
83
|
-
|
|
83
|
+
# absolute paths of the currently-open .include chain, to break include cycles
|
|
84
|
+
included = [nil]
|
|
85
|
+
while definition = get_definition(io_stack, included)
|
|
84
86
|
definition = clear_comments(definition)
|
|
85
87
|
next if definition.empty?
|
|
86
88
|
case definition
|
|
@@ -100,11 +102,16 @@ module OpenSSL
|
|
|
100
102
|
end
|
|
101
103
|
|
|
102
104
|
files.each do |filename|
|
|
105
|
+
real = File.expand_path(filename)
|
|
106
|
+
if included.include?(real)
|
|
107
|
+
raise ConfigError, "include cycle detected: '%s'" % filename
|
|
108
|
+
end
|
|
103
109
|
begin
|
|
104
110
|
io_stack << StringIO.new(File.read(filename))
|
|
105
111
|
rescue
|
|
106
112
|
raise ConfigError, "could not include file '%s'" % filename
|
|
107
113
|
end
|
|
114
|
+
included << real
|
|
108
115
|
end
|
|
109
116
|
when /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/
|
|
110
117
|
if $2
|
|
@@ -229,10 +236,10 @@ module OpenSSL
|
|
|
229
236
|
scanned.join
|
|
230
237
|
end
|
|
231
238
|
|
|
232
|
-
def get_definition(io_stack)
|
|
233
|
-
if line = get_line(io_stack)
|
|
239
|
+
def get_definition(io_stack, included = nil)
|
|
240
|
+
if line = get_line(io_stack, included)
|
|
234
241
|
while /[^\\]\\\z/ =~ line
|
|
235
|
-
if extra = get_line(io_stack)
|
|
242
|
+
if extra = get_line(io_stack, included)
|
|
236
243
|
line += extra
|
|
237
244
|
else
|
|
238
245
|
break
|
|
@@ -242,12 +249,13 @@ module OpenSSL
|
|
|
242
249
|
end
|
|
243
250
|
end
|
|
244
251
|
|
|
245
|
-
def get_line(io_stack)
|
|
252
|
+
def get_line(io_stack, included = nil)
|
|
246
253
|
while io = io_stack.last
|
|
247
254
|
if line = io.gets
|
|
248
255
|
return line.gsub(/[\r\n]*/, '')
|
|
249
256
|
end
|
|
250
257
|
io_stack.pop
|
|
258
|
+
included.pop if included
|
|
251
259
|
end
|
|
252
260
|
end
|
|
253
261
|
end
|
data/lib/openssl/ssl.rb
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
require "openssl/buffering"
|
|
14
14
|
require "io/nonblock"
|
|
15
|
+
require "ipaddr"
|
|
15
16
|
require "socket"
|
|
16
17
|
|
|
17
18
|
module OpenSSL
|
|
@@ -84,7 +85,6 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
|
|
|
84
85
|
|
|
85
86
|
DEFAULT_CERT_STORE = OpenSSL::X509::Store.new # :nodoc:
|
|
86
87
|
DEFAULT_CERT_STORE.set_default_paths
|
|
87
|
-
DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
|
|
88
88
|
|
|
89
89
|
# A callback invoked when DH parameters are required for ephemeral DH key
|
|
90
90
|
# exchange.
|
|
@@ -137,7 +137,7 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
|
|
|
137
137
|
# used.
|
|
138
138
|
def set_params(params={})
|
|
139
139
|
params = DEFAULT_PARAMS.merge(params)
|
|
140
|
-
self.options
|
|
140
|
+
self.options |= params.delete(:options) # set before min_version/max_version
|
|
141
141
|
params.each{ |name, value| self.__send__("#{name}=", value) }
|
|
142
142
|
if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
|
143
143
|
unless self.ca_file or self.ca_path or self.cert_store
|
|
@@ -319,15 +319,10 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
|
|
|
319
319
|
#when 7 # iPAddress in GeneralName (RFC5280)
|
|
320
320
|
elsif /\AIP(?: Address)?:(.*)/ =~ general_name
|
|
321
321
|
should_verify_common_name = false
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
# begin
|
|
327
|
-
# return true if $1 == IPAddr.new(hostname).to_s
|
|
328
|
-
# rescue IPAddr::InvalidAddressError
|
|
329
|
-
# end
|
|
330
|
-
# end
|
|
322
|
+
begin
|
|
323
|
+
return true if IPAddr.new($1) == IPAddr.new(hostname)
|
|
324
|
+
rescue IPAddr::InvalidAddressError
|
|
325
|
+
end
|
|
331
326
|
end
|
|
332
327
|
}
|
|
333
328
|
}
|
|
@@ -431,6 +426,13 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
|
|
|
431
426
|
# This method MUST be called after calling #connect to ensure that the
|
|
432
427
|
# hostname of a remote peer has been verified.
|
|
433
428
|
def post_connection_check(hostname)
|
|
429
|
+
# if connect already verified this hostname (via verify_hostname), skip;
|
|
430
|
+
# avoids double-verification in libraries that call post_connection_check
|
|
431
|
+
if defined?(@verified_hostname) && @verified_hostname == hostname
|
|
432
|
+
@verified_hostname = nil
|
|
433
|
+
return true
|
|
434
|
+
end
|
|
435
|
+
|
|
434
436
|
if peer_cert.nil?
|
|
435
437
|
msg = "Peer verification enabled, but no certificate received."
|
|
436
438
|
if using_anon_cipher?
|
|
@@ -446,6 +448,14 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
|
|
|
446
448
|
return true
|
|
447
449
|
end
|
|
448
450
|
|
|
451
|
+
# @private JRuby due having to call this outside of post_connection_check
|
|
452
|
+
def verify_certificate_identity_internal(hostname)
|
|
453
|
+
peer_cert = self.peer_cert
|
|
454
|
+
return nil if peer_cert.nil?
|
|
455
|
+
OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
|
|
456
|
+
end
|
|
457
|
+
private :verify_certificate_identity_internal
|
|
458
|
+
|
|
449
459
|
# call-seq:
|
|
450
460
|
# ssl.session -> aSession
|
|
451
461
|
#
|
data/lib/openssl.rb
CHANGED
|
@@ -1,3 +1,46 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'jopenssl/load'
|
|
4
|
+
|
|
5
|
+
module OpenSSL
|
|
6
|
+
autoload :Config, 'openssl/config' unless const_defined?(:Config, false)
|
|
7
|
+
autoload :ConfigError, 'openssl/config' unless const_defined?(:ConfigError, false)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
=begin
|
|
11
|
+
= Info
|
|
12
|
+
'OpenSSL for Ruby 2' project
|
|
13
|
+
Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
|
|
14
|
+
All rights reserved.
|
|
15
|
+
|
|
16
|
+
= Licence
|
|
17
|
+
This program is licensed under the same licence as Ruby.
|
|
18
|
+
(See the file 'COPYING'.)
|
|
19
|
+
=end
|
|
20
|
+
|
|
21
|
+
require 'openssl/bn'
|
|
22
|
+
require 'openssl/cipher'
|
|
23
|
+
require 'openssl/digest'
|
|
24
|
+
require 'openssl/hmac'
|
|
25
|
+
require 'openssl/pkcs5'
|
|
26
|
+
require 'openssl/pkey'
|
|
27
|
+
require 'openssl/ssl'
|
|
28
|
+
require 'openssl/x509'
|
|
29
|
+
|
|
30
|
+
module OpenSSL
|
|
31
|
+
# :call-seq:
|
|
32
|
+
# OpenSSL.secure_compare(string, string) -> true or false
|
|
33
|
+
#
|
|
34
|
+
# Constant time memory comparison. Inputs are hashed using SHA-256 to mask
|
|
35
|
+
# the length of the secret. Returns +true+ if the strings are identical,
|
|
36
|
+
# +false+ otherwise.
|
|
37
|
+
#
|
|
38
|
+
# This method is expensive due to the SHA-256 hashing. In most cases, where
|
|
39
|
+
# the input lengths are known to be equal or are not sensitive,
|
|
40
|
+
# OpenSSL.fixed_length_secure_compare should be used instead.
|
|
41
|
+
def self.secure_compare(a, b)
|
|
42
|
+
hashed_a = OpenSSL::Digest.digest('SHA256', a)
|
|
43
|
+
hashed_b = OpenSSL::Digest.digest('SHA256', b)
|
|
44
|
+
OpenSSL.fixed_length_secure_compare(hashed_a, hashed_b) && a == b
|
|
45
|
+
end
|
|
46
|
+
end
|