diff options
Diffstat (limited to 'ext/openssl/lib')
-rw-r--r-- | ext/openssl/lib/openssl.rb | 33 | ||||
-rw-r--r-- | ext/openssl/lib/openssl/bn.rb | 2 | ||||
-rw-r--r-- | ext/openssl/lib/openssl/buffering.rb | 33 | ||||
-rw-r--r-- | ext/openssl/lib/openssl/cipher.rb | 2 | ||||
-rw-r--r-- | ext/openssl/lib/openssl/config.rb | 7 | ||||
-rw-r--r-- | ext/openssl/lib/openssl/digest.rb | 27 | ||||
-rw-r--r-- | ext/openssl/lib/openssl/hmac.rb | 13 | ||||
-rw-r--r-- | ext/openssl/lib/openssl/pkcs5.rb | 2 | ||||
-rw-r--r-- | ext/openssl/lib/openssl/pkey.rb | 2 | ||||
-rw-r--r-- | ext/openssl/lib/openssl/ssl.rb | 42 | ||||
-rw-r--r-- | ext/openssl/lib/openssl/version.rb | 5 | ||||
-rw-r--r-- | ext/openssl/lib/openssl/x509.rb | 170 |
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 |