aboutsummaryrefslogtreecommitdiffstats
path: root/ext/openssl/lib
diff options
context:
space:
mode:
Diffstat (limited to 'ext/openssl/lib')
-rw-r--r--ext/openssl/lib/openssl.rb33
-rw-r--r--ext/openssl/lib/openssl/bn.rb2
-rw-r--r--ext/openssl/lib/openssl/buffering.rb33
-rw-r--r--ext/openssl/lib/openssl/cipher.rb2
-rw-r--r--ext/openssl/lib/openssl/config.rb7
-rw-r--r--ext/openssl/lib/openssl/digest.rb27
-rw-r--r--ext/openssl/lib/openssl/hmac.rb13
-rw-r--r--ext/openssl/lib/openssl/pkcs5.rb2
-rw-r--r--ext/openssl/lib/openssl/pkey.rb2
-rw-r--r--ext/openssl/lib/openssl/ssl.rb42
-rw-r--r--ext/openssl/lib/openssl/version.rb5
-rw-r--r--ext/openssl/lib/openssl/x509.rb170
12 files changed, 304 insertions, 34 deletions
diff --git a/ext/openssl/lib/openssl.rb b/ext/openssl/lib/openssl.rb
index 0914282920..7ba2229a1b 100644
--- a/ext/openssl/lib/openssl.rb
+++ b/ext/openssl/lib/openssl.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
=begin
= Info
'OpenSSL for Ruby 2' project
@@ -12,11 +12,26 @@
require 'openssl.so'
-require 'openssl/bn'
-require 'openssl/pkey'
-require 'openssl/cipher'
-require 'openssl/config'
-require 'openssl/digest'
-require 'openssl/x509'
-require 'openssl/ssl'
-require 'openssl/pkcs5'
+require_relative 'openssl/bn'
+require_relative 'openssl/pkey'
+require_relative 'openssl/cipher'
+require_relative 'openssl/config'
+require_relative 'openssl/digest'
+require_relative 'openssl/hmac'
+require_relative 'openssl/x509'
+require_relative 'openssl/ssl'
+require_relative 'openssl/pkcs5'
+
+module OpenSSL
+ # call-seq:
+ # OpenSSL.secure_compare(string, string) -> boolean
+ #
+ # Constant time memory comparison. Inputs are hashed using SHA-256 to mask
+ # the length of the secret. Returns +true+ if the strings are identical,
+ # +false+ otherwise.
+ def self.secure_compare(a, b)
+ hashed_a = OpenSSL::Digest::SHA256.digest(a)
+ hashed_b = OpenSSL::Digest::SHA256.digest(b)
+ OpenSSL.fixed_length_secure_compare(hashed_a, hashed_b) && a == b
+ end
+end
diff --git a/ext/openssl/lib/openssl/bn.rb b/ext/openssl/lib/openssl/bn.rb
index 8d1ebefb6e..0a5e11b4c2 100644
--- a/ext/openssl/lib/openssl/bn.rb
+++ b/ext/openssl/lib/openssl/bn.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
#
# = Ruby-space definitions that completes C-space funcs for BN
diff --git a/ext/openssl/lib/openssl/buffering.rb b/ext/openssl/lib/openssl/buffering.rb
index 5d1586e594..a5f4241bf4 100644
--- a/ext/openssl/lib/openssl/buffering.rb
+++ b/ext/openssl/lib/openssl/buffering.rb
@@ -1,5 +1,5 @@
# coding: binary
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
#= Info
# 'OpenSSL for Ruby 2' project
@@ -22,6 +22,29 @@
module OpenSSL::Buffering
include Enumerable
+ # A buffer which will retain binary encoding.
+ class Buffer < String
+ BINARY = Encoding::BINARY
+
+ def initialize
+ super
+
+ force_encoding(BINARY)
+ end
+
+ def << string
+ if string.encoding == BINARY
+ super(string)
+ else
+ super(string.b)
+ end
+
+ return self
+ end
+
+ alias concat <<
+ end
+
##
# The "sync mode" of the SSLSocket.
#
@@ -40,7 +63,7 @@ module OpenSSL::Buffering
def initialize(*)
super
@eof = false
- @rbuffer = ""
+ @rbuffer = Buffer.new
@sync = @io.sync
end
@@ -312,7 +335,7 @@ module OpenSSL::Buffering
# buffer is flushed to the underlying socket.
def do_write(s)
- @wbuffer = "" unless defined? @wbuffer
+ @wbuffer = Buffer.new unless defined? @wbuffer
@wbuffer << s
@wbuffer.force_encoding(Encoding::BINARY)
@sync ||= false
@@ -398,7 +421,7 @@ module OpenSSL::Buffering
# See IO#puts for full details.
def puts(*args)
- s = ""
+ s = Buffer.new
if args.empty?
s << "\n"
end
@@ -416,7 +439,7 @@ module OpenSSL::Buffering
# See IO#print for full details.
def print(*args)
- s = ""
+ s = Buffer.new
args.each{ |arg| s << arg.to_s }
do_write(s)
nil
diff --git a/ext/openssl/lib/openssl/cipher.rb b/ext/openssl/lib/openssl/cipher.rb
index af721b3a80..8ad8c35dd3 100644
--- a/ext/openssl/lib/openssl/cipher.rb
+++ b/ext/openssl/lib/openssl/cipher.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# = Ruby-space predefined Cipher subclasses
#
diff --git a/ext/openssl/lib/openssl/config.rb b/ext/openssl/lib/openssl/config.rb
index 48d8be0069..ef83c57b20 100644
--- a/ext/openssl/lib/openssl/config.rb
+++ b/ext/openssl/lib/openssl/config.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
=begin
= Ruby-space definitions that completes C-space funcs for Config
@@ -53,9 +53,8 @@ module OpenSSL
def parse_config(io)
begin
parse_config_lines(io)
- rescue ConfigError => e
- e.message.replace("error in line #{io.lineno}: " + e.message)
- raise
+ rescue => error
+ raise ConfigError, "error in line #{io.lineno}: " + error.message
end
end
diff --git a/ext/openssl/lib/openssl/digest.rb b/ext/openssl/lib/openssl/digest.rb
index b6744de6bd..c953911954 100644
--- a/ext/openssl/lib/openssl/digest.rb
+++ b/ext/openssl/lib/openssl/digest.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# = Ruby-space predefined Digest subclasses
#
@@ -15,11 +15,17 @@
module OpenSSL
class Digest
- alg = %w(MD2 MD4 MD5 MDC2 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512)
- if OPENSSL_VERSION_NUMBER < 0x10100000
- alg += %w(DSS DSS1 SHA)
+ # You can get a list of all algorithms:
+ # openssl list -digest-algorithms
+
+ ALGORITHMS = %w(MD4 MD5 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512)
+
+ if !OPENSSL_VERSION.include?("LibreSSL") && OPENSSL_VERSION_NUMBER > 0x10101000
+ ALGORITHMS.concat %w(BLAKE2b512 BLAKE2s256 SHA3-224 SHA3-256 SHA3-384 SHA3-512)
end
+ ALGORITHMS.freeze
+
# Return the hash value computed with _name_ Digest. _name_ is either the
# long name or short name of a supported digest algorithm.
#
@@ -35,17 +41,20 @@ module OpenSSL
super(data, name)
end
- alg.each{|name|
+ ALGORITHMS.each do |name|
klass = Class.new(self) {
define_method(:initialize, ->(data = nil) {super(name, data)})
}
+
singleton = (class << klass; self; end)
+
singleton.class_eval{
- define_method(:digest){|data| new.digest(data) }
- define_method(:hexdigest){|data| new.hexdigest(data) }
+ define_method(:digest) {|data| new.digest(data)}
+ define_method(:hexdigest) {|data| new.hexdigest(data)}
}
- const_set(name, klass)
- }
+
+ const_set(name.tr('-', '_'), klass)
+ end
# Deprecated.
#
diff --git a/ext/openssl/lib/openssl/hmac.rb b/ext/openssl/lib/openssl/hmac.rb
new file mode 100644
index 0000000000..3d4427611d
--- /dev/null
+++ b/ext/openssl/lib/openssl/hmac.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module OpenSSL
+ class HMAC
+ # Securely compare with another HMAC instance in constant time.
+ def ==(other)
+ return false unless HMAC === other
+ return false unless self.digest.bytesize == other.digest.bytesize
+
+ OpenSSL.fixed_length_secure_compare(self.digest, other.digest)
+ end
+ end
+end
diff --git a/ext/openssl/lib/openssl/pkcs5.rb b/ext/openssl/lib/openssl/pkcs5.rb
index 959447df5e..8dedc4beef 100644
--- a/ext/openssl/lib/openssl/pkcs5.rb
+++ b/ext/openssl/lib/openssl/pkcs5.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# Ruby/OpenSSL Project
# Copyright (C) 2017 Ruby/OpenSSL Project Authors
diff --git a/ext/openssl/lib/openssl/pkey.rb b/ext/openssl/lib/openssl/pkey.rb
index 8a547c340d..ecb112f792 100644
--- a/ext/openssl/lib/openssl/pkey.rb
+++ b/ext/openssl/lib/openssl/pkey.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# Ruby/OpenSSL Project
# Copyright (C) 2017 Ruby/OpenSSL Project Authors
diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb
index 355eb2ebbb..8554ada0bb 100644
--- a/ext/openssl/lib/openssl/ssl.rb
+++ b/ext/openssl/lib/openssl/ssl.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
=begin
= Info
'OpenSSL for Ruby 2' project
@@ -13,6 +13,7 @@
require "openssl/buffering"
require "io/nonblock"
require "ipaddr"
+require "socket"
module OpenSSL
module SSL
@@ -231,6 +232,11 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
end
module SocketForwarder
+ # The file descriptor for the socket.
+ def fileno
+ to_io.fileno
+ end
+
def addr
to_io.addr
end
@@ -435,6 +441,38 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
def session_get_cb
@context.session_get_cb
end
+
+ class << self
+
+ # call-seq:
+ # open(remote_host, remote_port, local_host=nil, local_port=nil, context: nil)
+ #
+ # Creates a new instance of SSLSocket.
+ # _remote\_host_ and _remote\_port_ are used to open TCPSocket.
+ # If _local\_host_ and _local\_port_ are specified,
+ # then those parameters are used on the local end to establish the connection.
+ # If _context_ is provided,
+ # the SSL Sockets initial params will be taken from the context.
+ #
+ # === Examples
+ #
+ # sock = OpenSSL::SSL::SSLSocket.open('localhost', 443)
+ # sock.connect # Initiates a connection to localhost:443
+ #
+ # with SSLContext:
+ #
+ # ctx = OpenSSL::SSL::SSLContext.new
+ # sock = OpenSSL::SSL::SSLSocket.open('localhost', 443, context: ctx)
+ # sock.connect # Initiates a connection to localhost:443 with SSLContext
+ def open(remote_host, remote_port, local_host=nil, local_port=nil, context: nil)
+ sock = ::TCPSocket.open(remote_host, remote_port, local_host, local_port)
+ if context.nil?
+ return OpenSSL::SSL::SSLSocket.new(sock)
+ else
+ return OpenSSL::SSL::SSLSocket.new(sock, context)
+ end
+ end
+ end
end
##
@@ -465,7 +503,7 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
end
# See TCPServer#listen for details.
- def listen(backlog=5)
+ def listen(backlog=Socket::SOMAXCONN)
@svr.listen(backlog)
end
diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb
new file mode 100644
index 0000000000..b07bb33009
--- /dev/null
+++ b/ext/openssl/lib/openssl/version.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module OpenSSL
+ VERSION = "2.2.0" unless defined?(VERSION)
+end
diff --git a/ext/openssl/lib/openssl/x509.rb b/ext/openssl/lib/openssl/x509.rb
index 98358f90da..1d2a5aaca8 100644
--- a/ext/openssl/lib/openssl/x509.rb
+++ b/ext/openssl/lib/openssl/x509.rb
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
#--
# = Ruby-space definitions that completes C-space funcs for X509 and subclasses
#
@@ -14,6 +14,22 @@
module OpenSSL
module X509
+ module Marshal
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+
+ module ClassMethods
+ def _load(string)
+ new(string)
+ end
+ end
+
+ def _dump(_level)
+ to_der
+ end
+ end
+
class ExtensionFactory
def create_extension(*arg)
if arg.size > 1
@@ -41,6 +57,8 @@ module OpenSSL
end
class Extension
+ include Marshal
+
def ==(other)
return false unless Extension === other
to_der == other.to_der
@@ -60,9 +78,146 @@ module OpenSSL
def to_a
[ self.oid, self.value, self.critical? ]
end
+
+ module Helpers
+ def find_extension(oid)
+ extensions.find { |e| e.oid == oid }
+ end
+ end
+
+ module SubjectKeyIdentifier
+ include Helpers
+
+ # Get the subject's key identifier from the subjectKeyIdentifier
+ # exteension, as described in RFC5280 Section 4.2.1.2.
+ #
+ # Returns the binary String key identifier or nil or raises
+ # ASN1::ASN1Error.
+ def subject_key_identifier
+ ext = find_extension("subjectKeyIdentifier")
+ return nil if ext.nil?
+
+ ski_asn1 = ASN1.decode(ext.value_der)
+ if ext.critical? || ski_asn1.tag_class != :UNIVERSAL || ski_asn1.tag != ASN1::OCTET_STRING
+ raise ASN1::ASN1Error, "invalid extension"
+ end
+
+ ski_asn1.value
+ end
+ end
+
+ module AuthorityKeyIdentifier
+ include Helpers
+
+ # Get the issuing certificate's key identifier from the
+ # authorityKeyIdentifier extension, as described in RFC5280
+ # Section 4.2.1.1
+ #
+ # Returns the binary String keyIdentifier or nil or raises
+ # ASN1::ASN1Error.
+ def authority_key_identifier
+ ext = find_extension("authorityKeyIdentifier")
+ return nil if ext.nil?
+
+ aki_asn1 = ASN1.decode(ext.value_der)
+ if ext.critical? || aki_asn1.tag_class != :UNIVERSAL || aki_asn1.tag != ASN1::SEQUENCE
+ raise ASN1::ASN1Error, "invalid extension"
+ end
+
+ key_id = aki_asn1.value.find do |v|
+ v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0
+ end
+
+ key_id.nil? ? nil : key_id.value
+ end
+ end
+
+ module CRLDistributionPoints
+ include Helpers
+
+ # Get the distributionPoint fullName URI from the certificate's CRL
+ # distribution points extension, as described in RFC5280 Section
+ # 4.2.1.13
+ #
+ # Returns an array of strings or nil or raises ASN1::ASN1Error.
+ def crl_uris
+ ext = find_extension("crlDistributionPoints")
+ return nil if ext.nil?
+
+ cdp_asn1 = ASN1.decode(ext.value_der)
+ if cdp_asn1.tag_class != :UNIVERSAL || cdp_asn1.tag != ASN1::SEQUENCE
+ raise ASN1::ASN1Error, "invalid extension"
+ end
+
+ crl_uris = cdp_asn1.map do |crl_distribution_point|
+ distribution_point = crl_distribution_point.value.find do |v|
+ v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0
+ end
+ full_name = distribution_point&.value&.find do |v|
+ v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0
+ end
+ full_name&.value&.find do |v|
+ v.tag_class == :CONTEXT_SPECIFIC && v.tag == 6 # uniformResourceIdentifier
+ end
+ end
+
+ crl_uris&.map(&:value)
+ end
+ end
+
+ module AuthorityInfoAccess
+ include Helpers
+
+ # Get the information and services for the issuer from the certificate's
+ # authority information access extension exteension, as described in RFC5280
+ # Section 4.2.2.1.
+ #
+ # Returns an array of strings or nil or raises ASN1::ASN1Error.
+ def ca_issuer_uris
+ aia_asn1 = parse_aia_asn1
+ return nil if aia_asn1.nil?
+
+ ca_issuer = aia_asn1.value.select do |authority_info_access|
+ authority_info_access.value.first.value == "caIssuers"
+ end
+
+ ca_issuer&.map(&:value)&.map(&:last)&.map(&:value)
+ end
+
+ # Get the URIs for OCSP from the certificate's authority information access
+ # extension exteension, as described in RFC5280 Section 4.2.2.1.
+ #
+ # Returns an array of strings or nil or raises ASN1::ASN1Error.
+ def ocsp_uris
+ aia_asn1 = parse_aia_asn1
+ return nil if aia_asn1.nil?
+
+ ocsp = aia_asn1.value.select do |authority_info_access|
+ authority_info_access.value.first.value == "OCSP"
+ end
+
+ ocsp&.map(&:value)&.map(&:last)&.map(&:value)
+ end
+
+ private
+
+ def parse_aia_asn1
+ ext = find_extension("authorityInfoAccess")
+ return nil if ext.nil?
+
+ aia_asn1 = ASN1.decode(ext.value_der)
+ if ext.critical? || aia_asn1.tag_class != :UNIVERSAL || aia_asn1.tag != ASN1::SEQUENCE
+ raise ASN1::ASN1Error, "invalid extension"
+ end
+
+ aia_asn1
+ end
+ end
end
class Name
+ include Marshal
+
module RFC2253DN
Special = ',=+<>#;'
HexChar = /[0-9a-fA-F]/
@@ -166,6 +321,8 @@ module OpenSSL
end
class Attribute
+ include Marshal
+
def ==(other)
return false unless Attribute === other
to_der == other.to_der
@@ -179,6 +336,12 @@ module OpenSSL
end
class Certificate
+ include Marshal
+ include Extension::SubjectKeyIdentifier
+ include Extension::AuthorityKeyIdentifier
+ include Extension::CRLDistributionPoints
+ include Extension::AuthorityInfoAccess
+
def pretty_print(q)
q.object_group(self) {
q.breakable
@@ -192,6 +355,9 @@ module OpenSSL
end
class CRL
+ include Marshal
+ include Extension::AuthorityKeyIdentifier
+
def ==(other)
return false unless CRL === other
to_der == other.to_der
@@ -206,6 +372,8 @@ module OpenSSL
end
class Request
+ include Marshal
+
def ==(other)
return false unless Request === other
to_der == other.to_der