From fcbf63e62c627deae76c1b8cb8c0876c536ed811 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 16 Mar 2020 18:49:26 +0900 Subject: Fresh start --- jni/ruby/ext/openssl/lib/openssl.rb | 24 ++ jni/ruby/ext/openssl/lib/openssl/bn.rb | 45 +++ jni/ruby/ext/openssl/lib/openssl/buffering.rb | 457 +++++++++++++++++++++++++ jni/ruby/ext/openssl/lib/openssl/cipher.rb | 65 ++++ jni/ruby/ext/openssl/lib/openssl/config.rb | 472 ++++++++++++++++++++++++++ jni/ruby/ext/openssl/lib/openssl/digest.rb | 88 +++++ jni/ruby/ext/openssl/lib/openssl/ssl.rb | 324 ++++++++++++++++++ jni/ruby/ext/openssl/lib/openssl/x509.rb | 182 ++++++++++ 8 files changed, 1657 insertions(+) create mode 100644 jni/ruby/ext/openssl/lib/openssl.rb create mode 100644 jni/ruby/ext/openssl/lib/openssl/bn.rb create mode 100644 jni/ruby/ext/openssl/lib/openssl/buffering.rb create mode 100644 jni/ruby/ext/openssl/lib/openssl/cipher.rb create mode 100644 jni/ruby/ext/openssl/lib/openssl/config.rb create mode 100644 jni/ruby/ext/openssl/lib/openssl/digest.rb create mode 100644 jni/ruby/ext/openssl/lib/openssl/ssl.rb create mode 100644 jni/ruby/ext/openssl/lib/openssl/x509.rb (limited to 'jni/ruby/ext/openssl/lib') diff --git a/jni/ruby/ext/openssl/lib/openssl.rb b/jni/ruby/ext/openssl/lib/openssl.rb new file mode 100644 index 0000000..adcaee7 --- /dev/null +++ b/jni/ruby/ext/openssl/lib/openssl.rb @@ -0,0 +1,24 @@ +=begin += $RCSfile$ -- Loader for all OpenSSL C-space and Ruby-space definitions + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id: openssl.rb 32664 2011-07-25 06:30:07Z nahi $ +=end + +require 'openssl.so' + +require 'openssl/bn' +require 'openssl/cipher' +require 'openssl/config' +require 'openssl/digest' +require 'openssl/x509' +require 'openssl/ssl' diff --git a/jni/ruby/ext/openssl/lib/openssl/bn.rb b/jni/ruby/ext/openssl/lib/openssl/bn.rb new file mode 100644 index 0000000..8c07eac --- /dev/null +++ b/jni/ruby/ext/openssl/lib/openssl/bn.rb @@ -0,0 +1,45 @@ +#-- +# +# $RCSfile$ +# +# = Ruby-space definitions that completes C-space funcs for BN +# +# = Info +# 'OpenSSL for Ruby 2' project +# Copyright (C) 2002 Michal Rokos +# All rights reserved. +# +# = Licence +# This program is licenced under the same licence as Ruby. +# (See the file 'LICENCE'.) +# +# = Version +# $Id: bn.rb 47647 2014-09-20 01:17:05Z akr $ +# +#++ + +module OpenSSL + class BN + include Comparable + + def pretty_print(q) + q.object_group(self) { + q.text ' ' + q.text to_i.to_s + } + end + end # BN +end # OpenSSL + +## +# Add double dispatch to Integer +# +class Integer + # Casts an Integer as an OpenSSL::BN + # + # See `man bn` for more info. + def to_bn + OpenSSL::BN::new(self) + end +end # Integer + diff --git a/jni/ruby/ext/openssl/lib/openssl/buffering.rb b/jni/ruby/ext/openssl/lib/openssl/buffering.rb new file mode 100644 index 0000000..40bbd0f --- /dev/null +++ b/jni/ruby/ext/openssl/lib/openssl/buffering.rb @@ -0,0 +1,457 @@ +# coding: binary +#-- +#= $RCSfile$ -- Buffering mix-in module. +# +#= Info +# 'OpenSSL for Ruby 2' project +# Copyright (C) 2001 GOTOU YUUZOU +# All rights reserved. +# +#= Licence +# This program is licenced under the same licence as Ruby. +# (See the file 'LICENCE'.) +# +#= Version +# $Id: buffering.rb 43964 2013-12-03 01:44:41Z drbrain $ +#++ + +## +# OpenSSL IO buffering mix-in module. +# +# This module allows an OpenSSL::SSL::SSLSocket to behave like an IO. +# +# You typically won't use this module directly, you can see it implemented in +# OpenSSL::SSL::SSLSocket. + +module OpenSSL::Buffering + include Enumerable + + ## + # The "sync mode" of the SSLSocket. + # + # See IO#sync for full details. + + attr_accessor :sync + + ## + # Default size to read from or write to the SSLSocket for buffer operations. + + BLOCK_SIZE = 1024*16 + + ## + # Creates an instance of OpenSSL's buffering IO module. + + def initialize(*) + super + @eof = false + @rbuffer = "" + @sync = @io.sync + end + + # + # for reading. + # + private + + ## + # Fills the buffer from the underlying SSLSocket + + def fill_rbuff + begin + @rbuffer << self.sysread(BLOCK_SIZE) + rescue Errno::EAGAIN + retry + rescue EOFError + @eof = true + end + end + + ## + # Consumes +size+ bytes from the buffer + + def consume_rbuff(size=nil) + if @rbuffer.empty? + nil + else + size = @rbuffer.size unless size + ret = @rbuffer[0, size] + @rbuffer[0, size] = "" + ret + end + end + + public + + ## + # Reads +size+ bytes from the stream. If +buf+ is provided it must + # reference a string which will receive the data. + # + # See IO#read for full details. + + def read(size=nil, buf=nil) + if size == 0 + if buf + buf.clear + return buf + else + return "" + end + end + until @eof + break if size && size <= @rbuffer.size + fill_rbuff + end + ret = consume_rbuff(size) || "" + if buf + buf.replace(ret) + ret = buf + end + (size && ret.empty?) ? nil : ret + end + + ## + # Reads at most +maxlen+ bytes from the stream. If +buf+ is provided it + # must reference a string which will receive the data. + # + # See IO#readpartial for full details. + + def readpartial(maxlen, buf=nil) + if maxlen == 0 + if buf + buf.clear + return buf + else + return "" + end + end + if @rbuffer.empty? + begin + return sysread(maxlen, buf) + rescue Errno::EAGAIN + retry + end + end + ret = consume_rbuff(maxlen) + if buf + buf.replace(ret) + ret = buf + end + raise EOFError if ret.empty? + ret + end + + ## + # Reads at most +maxlen+ bytes in the non-blocking manner. + # + # When no data can be read without blocking it raises + # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable. + # + # IO::WaitReadable means SSL needs to read internally so read_nonblock + # should be called again when the underlying IO is readable. + # + # IO::WaitWritable means SSL needs to write internally so read_nonblock + # should be called again after the underlying IO is writable. + # + # OpenSSL::Buffering#read_nonblock needs two rescue clause as follows: + # + # # emulates blocking read (readpartial). + # begin + # result = ssl.read_nonblock(maxlen) + # rescue IO::WaitReadable + # IO.select([io]) + # retry + # rescue IO::WaitWritable + # IO.select(nil, [io]) + # retry + # end + # + # Note that one reason that read_nonblock writes to the underlying IO is + # when the peer requests a new TLS/SSL handshake. See openssl the FAQ for + # more details. http://www.openssl.org/support/faq.html + + def read_nonblock(maxlen, buf=nil, exception: true) + if maxlen == 0 + if buf + buf.clear + return buf + else + return "" + end + end + if @rbuffer.empty? + return sysread_nonblock(maxlen, buf, exception: exception) + end + ret = consume_rbuff(maxlen) + if buf + buf.replace(ret) + ret = buf + end + raise EOFError if ret.empty? + ret + end + + ## + # Reads the next "line+ from the stream. Lines are separated by +eol+. If + # +limit+ is provided the result will not be longer than the given number of + # bytes. + # + # +eol+ may be a String or Regexp. + # + # Unlike IO#gets the line read will not be assigned to +$_+. + # + # Unlike IO#gets the separator must be provided if a limit is provided. + + def gets(eol=$/, limit=nil) + idx = @rbuffer.index(eol) + until @eof + break if idx + fill_rbuff + idx = @rbuffer.index(eol) + end + if eol.is_a?(Regexp) + size = idx ? idx+$&.size : nil + else + size = idx ? idx+eol.size : nil + end + if limit and limit >= 0 + size = [size, limit].min + end + consume_rbuff(size) + end + + ## + # Executes the block for every line in the stream where lines are separated + # by +eol+. + # + # See also #gets + + def each(eol=$/) + while line = self.gets(eol) + yield line + end + end + alias each_line each + + ## + # Reads lines from the stream which are separated by +eol+. + # + # See also #gets + + def readlines(eol=$/) + ary = [] + while line = self.gets(eol) + ary << line + end + ary + end + + ## + # Reads a line from the stream which is separated by +eol+. + # + # Raises EOFError if at end of file. + + def readline(eol=$/) + raise EOFError if eof? + gets(eol) + end + + ## + # Reads one character from the stream. Returns nil if called at end of + # file. + + def getc + read(1) + end + + ## + # Calls the given block once for each byte in the stream. + + def each_byte # :yields: byte + while c = getc + yield(c.ord) + end + end + + ## + # Reads a one-character string from the stream. Raises an EOFError at end + # of file. + + def readchar + raise EOFError if eof? + getc + end + + ## + # Pushes character +c+ back onto the stream such that a subsequent buffered + # character read will return it. + # + # Unlike IO#getc multiple bytes may be pushed back onto the stream. + # + # Has no effect on unbuffered reads (such as #sysread). + + def ungetc(c) + @rbuffer[0,0] = c.chr + end + + ## + # Returns true if the stream is at file which means there is no more data to + # be read. + + def eof? + fill_rbuff if !@eof && @rbuffer.empty? + @eof && @rbuffer.empty? + end + alias eof eof? + + # + # for writing. + # + private + + ## + # Writes +s+ to the buffer. When the buffer is full or #sync is true the + # buffer is flushed to the underlying socket. + + def do_write(s) + @wbuffer = "" unless defined? @wbuffer + @wbuffer << s + @wbuffer.force_encoding(Encoding::BINARY) + @sync ||= false + if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/) + remain = idx ? idx + $/.size : @wbuffer.length + nwritten = 0 + while remain > 0 + str = @wbuffer[nwritten,remain] + begin + nwrote = syswrite(str) + rescue Errno::EAGAIN + retry + end + remain -= nwrote + nwritten += nwrote + end + @wbuffer[0,nwritten] = "" + end + end + + public + + ## + # Writes +s+ to the stream. If the argument is not a string it will be + # converted using String#to_s. Returns the number of bytes written. + + def write(s) + do_write(s) + s.bytesize + end + + ## + # Writes +str+ in the non-blocking manner. + # + # If there is buffered data, it is flushed first. This may block. + # + # write_nonblock returns number of bytes written to the SSL connection. + # + # When no data can be written without blocking it raises + # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable. + # + # IO::WaitReadable means SSL needs to read internally so write_nonblock + # should be called again after the underlying IO is readable. + # + # IO::WaitWritable means SSL needs to write internally so write_nonblock + # should be called again after underlying IO is writable. + # + # So OpenSSL::Buffering#write_nonblock needs two rescue clause as follows. + # + # # emulates blocking write. + # begin + # result = ssl.write_nonblock(str) + # rescue IO::WaitReadable + # IO.select([io]) + # retry + # rescue IO::WaitWritable + # IO.select(nil, [io]) + # retry + # end + # + # Note that one reason that write_nonblock reads from the underlying IO + # is when the peer requests a new TLS/SSL handshake. See the openssl FAQ + # for more details. http://www.openssl.org/support/faq.html + + def write_nonblock(s, exception: true) + flush + syswrite_nonblock(s, exception: exception) + end + + ## + # Writes +s+ to the stream. +s+ will be converted to a String using + # String#to_s. + + def << (s) + do_write(s) + self + end + + ## + # Writes +args+ to the stream along with a record separator. + # + # See IO#puts for full details. + + def puts(*args) + s = "" + if args.empty? + s << "\n" + end + args.each{|arg| + s << arg.to_s + if $/ && /\n\z/ !~ s + s << "\n" + end + } + do_write(s) + nil + end + + ## + # Writes +args+ to the stream. + # + # See IO#print for full details. + + def print(*args) + s = "" + args.each{ |arg| s << arg.to_s } + do_write(s) + nil + end + + ## + # Formats and writes to the stream converting parameters under control of + # the format string. + # + # See Kernel#sprintf for format string details. + + def printf(s, *args) + do_write(s % args) + nil + end + + ## + # Flushes buffered data to the SSLSocket. + + def flush + osync = @sync + @sync = true + do_write "" + return self + ensure + @sync = osync + end + + ## + # Closes the SSLSocket and flushes any unwritten data. + + def close + flush rescue nil + sysclose + end +end diff --git a/jni/ruby/ext/openssl/lib/openssl/cipher.rb b/jni/ruby/ext/openssl/lib/openssl/cipher.rb new file mode 100644 index 0000000..c32cea2 --- /dev/null +++ b/jni/ruby/ext/openssl/lib/openssl/cipher.rb @@ -0,0 +1,65 @@ +#-- +# +# $RCSfile$ +# +# = Ruby-space predefined Cipher subclasses +# +# = Info +# 'OpenSSL for Ruby 2' project +# Copyright (C) 2002 Michal Rokos +# All rights reserved. +# +# = Licence +# This program is licenced under the same licence as Ruby. +# (See the file 'LICENCE'.) +# +# = Version +# $Id: cipher.rb 36895 2012-09-04 00:57:31Z nobu $ +# +#++ + +module OpenSSL + class Cipher + %w(AES CAST5 BF DES IDEA RC2 RC4 RC5).each{|name| + klass = Class.new(Cipher){ + define_method(:initialize){|*args| + cipher_name = args.inject(name){|n, arg| "#{n}-#{arg}" } + super(cipher_name) + } + } + const_set(name, klass) + } + + %w(128 192 256).each{|keylen| + klass = Class.new(Cipher){ + define_method(:initialize){|mode| + mode ||= "CBC" + cipher_name = "AES-#{keylen}-#{mode}" + super(cipher_name) + } + } + const_set("AES#{keylen}", klass) + } + + # Generate, set, and return a random key. + # You must call cipher.encrypt or cipher.decrypt before calling this method. + def random_key + str = OpenSSL::Random.random_bytes(self.key_len) + self.key = str + return str + end + + # Generate, set, and return a random iv. + # You must call cipher.encrypt or cipher.decrypt before calling this method. + def random_iv + str = OpenSSL::Random.random_bytes(self.iv_len) + self.iv = str + return str + end + + # This class is only provided for backwards compatibility. Use OpenSSL::Cipher in the future. + class Cipher < Cipher + # add warning + end + end # Cipher +end # OpenSSL diff --git a/jni/ruby/ext/openssl/lib/openssl/config.rb b/jni/ruby/ext/openssl/lib/openssl/config.rb new file mode 100644 index 0000000..5716d59 --- /dev/null +++ b/jni/ruby/ext/openssl/lib/openssl/config.rb @@ -0,0 +1,472 @@ +=begin += Ruby-space definitions that completes C-space funcs for Config + += Info + Copyright (C) 2010 Hiroshi Nakamura + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + +=end + +require 'stringio' + +module OpenSSL + ## + # = OpenSSL::Config + # + # Configuration for the openssl library. + # + # Many system's installation of openssl library will depend on your system + # configuration. See the value of OpenSSL::Config::DEFAULT_CONFIG_FILE for + # the location of the file for your host. + # + # See also http://www.openssl.org/docs/apps/config.html + class Config + include Enumerable + + class << self + + ## + # Parses a given +string+ as a blob that contains configuration for openssl. + # + # If the source of the IO is a file, then consider using #parse_config. + def parse(string) + c = new() + parse_config(StringIO.new(string)).each do |section, hash| + c[section] = hash + end + c + end + + ## + # load is an alias to ::new + alias load new + + ## + # Parses the configuration data read from +io+, see also #parse. + # + # Raises a ConfigError on invalid configuration data. + def parse_config(io) + begin + parse_config_lines(io) + rescue ConfigError => e + e.message.replace("error in line #{io.lineno}: " + e.message) + raise + end + end + + def get_key_string(data, section, key) # :nodoc: + if v = data[section] && data[section][key] + return v + elsif section == 'ENV' + if v = ENV[key] + return v + end + end + if v = data['default'] && data['default'][key] + return v + end + end + + private + + def parse_config_lines(io) + section = 'default' + data = {section => {}} + while definition = get_definition(io) + definition = clear_comments(definition) + next if definition.empty? + if definition[0] == ?[ + if /\[([^\]]*)\]/ =~ definition + section = $1.strip + data[section] ||= {} + else + raise ConfigError, "missing close square bracket" + end + else + if /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/ =~ definition + if $2 + section = $1 + key = $2 + else + key = $1 + end + value = unescape_value(data, section, $3) + (data[section] ||= {})[key] = value.strip + else + raise ConfigError, "missing equal sign" + end + end + end + data + end + + # escape with backslash + QUOTE_REGEXP_SQ = /\A([^'\\]*(?:\\.[^'\\]*)*)'/ + # escape with backslash and doubled dq + QUOTE_REGEXP_DQ = /\A([^"\\]*(?:""[^"\\]*|\\.[^"\\]*)*)"/ + # escaped char map + ESCAPE_MAP = { + "r" => "\r", + "n" => "\n", + "b" => "\b", + "t" => "\t", + } + + def unescape_value(data, section, value) + scanned = [] + while m = value.match(/['"\\$]/) + scanned << m.pre_match + c = m[0] + value = m.post_match + case c + when "'" + if m = value.match(QUOTE_REGEXP_SQ) + scanned << m[1].gsub(/\\(.)/, '\\1') + value = m.post_match + else + break + end + when '"' + if m = value.match(QUOTE_REGEXP_DQ) + scanned << m[1].gsub(/""/, '').gsub(/\\(.)/, '\\1') + value = m.post_match + else + break + end + when "\\" + c = value.slice!(0, 1) + scanned << (ESCAPE_MAP[c] || c) + when "$" + ref, value = extract_reference(value) + refsec = section + if ref.index('::') + refsec, ref = ref.split('::', 2) + end + if v = get_key_string(data, refsec, ref) + scanned << v + else + raise ConfigError, "variable has no value" + end + else + raise 'must not reaced' + end + end + scanned << value + scanned.join + end + + def extract_reference(value) + rest = '' + if m = value.match(/\(([^)]*)\)|\{([^}]*)\}/) + value = m[1] || m[2] + rest = m.post_match + elsif [?(, ?{].include?(value[0]) + raise ConfigError, "no close brace" + end + if m = value.match(/[a-zA-Z0-9_]*(?:::[a-zA-Z0-9_]*)?/) + return m[0], m.post_match + rest + else + raise + end + end + + def clear_comments(line) + # FCOMMENT + if m = line.match(/\A([\t\n\f ]*);.*\z/) + return m[1] + end + # COMMENT + scanned = [] + while m = line.match(/[#'"\\]/) + scanned << m.pre_match + c = m[0] + line = m.post_match + case c + when '#' + line = nil + break + when "'", '"' + regexp = (c == "'") ? QUOTE_REGEXP_SQ : QUOTE_REGEXP_DQ + scanned << c + if m = line.match(regexp) + scanned << m[0] + line = m.post_match + else + scanned << line + line = nil + break + end + when "\\" + scanned << c + scanned << line.slice!(0, 1) + else + raise 'must not reaced' + end + end + scanned << line + scanned.join + end + + def get_definition(io) + if line = get_line(io) + while /[^\\]\\\z/ =~ line + if extra = get_line(io) + line += extra + else + break + end + end + return line.strip + end + end + + def get_line(io) + if line = io.gets + line.gsub(/[\r\n]*/, '') + end + end + end + + ## + # Creates an instance of OpenSSL's configuration class. + # + # This can be used in contexts like OpenSSL::X509::ExtensionFactory.config= + # + # If the optional +filename+ parameter is provided, then it is read in and + # parsed via #parse_config. + # + # This can raise IO exceptions based on the access, or availability of the + # file. A ConfigError exception may be raised depending on the validity of + # the data being configured. + # + def initialize(filename = nil) + @data = {} + if filename + File.open(filename.to_s) do |file| + Config.parse_config(file).each do |section, hash| + self[section] = hash + end + end + end + end + + ## + # Gets the value of +key+ from the given +section+ + # + # Given the following configurating file being loaded: + # + # config = OpenSSL::Config.load('foo.cnf') + # #=> # + # puts config.to_s + # #=> [ default ] + # # foo=bar + # + # You can get a specific value from the config if you know the +section+ + # and +key+ like so: + # + # config.get_value('default','foo') + # #=> "bar" + # + def get_value(section, key) + if section.nil? + raise TypeError.new('nil not allowed') + end + section = 'default' if section.empty? + get_key_string(section, key) + end + + ## + # + # *Deprecated* + # + # Use #get_value instead + def value(arg1, arg2 = nil) # :nodoc: + warn('Config#value is deprecated; use Config#get_value') + if arg2.nil? + section, key = 'default', arg1 + else + section, key = arg1, arg2 + end + section ||= 'default' + section = 'default' if section.empty? + get_key_string(section, key) + end + + ## + # Set the target +key+ with a given +value+ under a specific +section+. + # + # Given the following configurating file being loaded: + # + # config = OpenSSL::Config.load('foo.cnf') + # #=> # + # puts config.to_s + # #=> [ default ] + # # foo=bar + # + # You can set the value of +foo+ under the +default+ section to a new + # value: + # + # config.add_value('default', 'foo', 'buzz') + # #=> "buzz" + # puts config.to_s + # #=> [ default ] + # # foo=buzz + # + def add_value(section, key, value) + check_modify + (@data[section] ||= {})[key] = value + end + + ## + # Get a specific +section+ from the current configuration + # + # Given the following configurating file being loaded: + # + # config = OpenSSL::Config.load('foo.cnf') + # #=> # + # puts config.to_s + # #=> [ default ] + # # foo=bar + # + # You can get a hash of the specific section like so: + # + # config['default'] + # #=> {"foo"=>"bar"} + # + def [](section) + @data[section] || {} + end + + ## + # Deprecated + # + # Use #[] instead + def section(name) # :nodoc: + warn('Config#section is deprecated; use Config#[]') + @data[name] || {} + end + + ## + # Sets a specific +section+ name with a Hash +pairs+ + # + # Given the following configuration being created: + # + # config = OpenSSL::Config.new + # #=> # + # config['default'] = {"foo"=>"bar","baz"=>"buz"} + # #=> {"foo"=>"bar", "baz"=>"buz"} + # puts config.to_s + # #=> [ default ] + # # foo=bar + # # baz=buz + # + # It's important to note that this will essentially merge any of the keys + # in +pairs+ with the existing +section+. For example: + # + # config['default'] + # #=> {"foo"=>"bar", "baz"=>"buz"} + # config['default'] = {"foo" => "changed"} + # #=> {"foo"=>"changed"} + # config['default'] + # #=> {"foo"=>"changed", "baz"=>"buz"} + # + def []=(section, pairs) + check_modify + @data[section] ||= {} + pairs.each do |key, value| + self.add_value(section, key, value) + end + end + + ## + # Get the names of all sections in the current configuration + def sections + @data.keys + end + + ## + # Get the parsable form of the current configuration + # + # Given the following configuration being created: + # + # config = OpenSSL::Config.new + # #=> # + # config['default'] = {"foo"=>"bar","baz"=>"buz"} + # #=> {"foo"=>"bar", "baz"=>"buz"} + # puts config.to_s + # #=> [ default ] + # # foo=bar + # # baz=buz + # + # You can parse get the serialized configuration using #to_s and then parse + # it later: + # + # serialized_config = config.to_s + # # much later... + # new_config = OpenSSL::Config.parse(serialized_config) + # #=> # + # puts new_config + # #=> [ default ] + # foo=bar + # baz=buz + # + def to_s + ary = [] + @data.keys.sort.each do |section| + ary << "[ #{section} ]\n" + @data[section].keys.each do |key| + ary << "#{key}=#{@data[section][key]}\n" + end + ary << "\n" + end + ary.join + end + + ## + # For a block. + # + # Receive the section and its pairs for the current configuration. + # + # config.each do |section, key, value| + # # ... + # end + # + def each + @data.each do |section, hash| + hash.each do |key, value| + yield [section, key, value] + end + end + end + + ## + # String representation of this configuration object, including the class + # name and its sections. + def inspect + "#<#{self.class.name} sections=#{sections.inspect}>" + end + + protected + + def data # :nodoc: + @data + end + + private + + def initialize_copy(other) + @data = other.data.dup + end + + def check_modify + raise TypeError.new("Insecure: can't modify OpenSSL config") if frozen? + end + + def get_key_string(section, key) + Config.get_key_string(@data, section, key) + end + end +end diff --git a/jni/ruby/ext/openssl/lib/openssl/digest.rb b/jni/ruby/ext/openssl/lib/openssl/digest.rb new file mode 100644 index 0000000..f63892a --- /dev/null +++ b/jni/ruby/ext/openssl/lib/openssl/digest.rb @@ -0,0 +1,88 @@ +#-- +# +# $RCSfile$ +# +# = Ruby-space predefined Digest subclasses +# +# = Info +# 'OpenSSL for Ruby 2' project +# Copyright (C) 2002 Michal Rokos +# All rights reserved. +# +# = Licence +# This program is licenced under the same licence as Ruby. +# (See the file 'LICENCE'.) +# +# = Version +# $Id: digest.rb 44116 2013-12-10 07:16:03Z nobu $ +# +#++ + +module OpenSSL + class Digest + + alg = %w(DSS DSS1 MD2 MD4 MD5 MDC2 RIPEMD160 SHA SHA1) + if OPENSSL_VERSION_NUMBER > 0x00908000 + alg += %w(SHA224 SHA256 SHA384 SHA512) + end + + # Return the +data+ hash computed with +name+ Digest. +name+ is either the + # long name or short name of a supported digest algorithm. + # + # === Examples + # + # OpenSSL::Digest.digest("SHA256", "abc") + # + # which is equivalent to: + # + # OpenSSL::Digest::SHA256.digest("abc") + + def self.digest(name, data) + super(data, name) + end + + alg.each{|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) } + } + const_set(name, klass) + } + + # Deprecated. + # + # This class is only provided for backwards compatibility. + class Digest < Digest # :nodoc: + # Deprecated. + # + # See OpenSSL::Digest.new + def initialize(*args) + warn('Digest::Digest is deprecated; use Digest') + super(*args) + end + end + + end # Digest + + # Returns a Digest subclass by +name+. + # + # require 'openssl' + # + # OpenSSL::Digest("MD5") + # # => OpenSSL::Digest::MD5 + # + # Digest("Foo") + # # => NameError: wrong constant name Foo + + def Digest(name) + OpenSSL::Digest.const_get(name) + end + + module_function :Digest + +end # OpenSSL + diff --git a/jni/ruby/ext/openssl/lib/openssl/ssl.rb b/jni/ruby/ext/openssl/lib/openssl/ssl.rb new file mode 100644 index 0000000..fdd037b --- /dev/null +++ b/jni/ruby/ext/openssl/lib/openssl/ssl.rb @@ -0,0 +1,324 @@ +=begin += $RCSfile$ -- Ruby-space definitions that completes C-space funcs for SSL + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU YUUZOU + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id: ssl.rb 51554 2015-08-12 15:16:42Z nagachika $ +=end + +require "openssl/buffering" +require "fcntl" + +module OpenSSL + module SSL + class SSLContext + DEFAULT_PARAMS = { + :ssl_version => "SSLv23", + :verify_mode => OpenSSL::SSL::VERIFY_PEER, + :ciphers => %w{ + ECDHE-ECDSA-AES128-GCM-SHA256 + ECDHE-RSA-AES128-GCM-SHA256 + ECDHE-ECDSA-AES256-GCM-SHA384 + ECDHE-RSA-AES256-GCM-SHA384 + DHE-RSA-AES128-GCM-SHA256 + DHE-DSS-AES128-GCM-SHA256 + DHE-RSA-AES256-GCM-SHA384 + DHE-DSS-AES256-GCM-SHA384 + ECDHE-ECDSA-AES128-SHA256 + ECDHE-RSA-AES128-SHA256 + ECDHE-ECDSA-AES128-SHA + ECDHE-RSA-AES128-SHA + ECDHE-ECDSA-AES256-SHA384 + ECDHE-RSA-AES256-SHA384 + ECDHE-ECDSA-AES256-SHA + ECDHE-RSA-AES256-SHA + DHE-RSA-AES128-SHA256 + DHE-RSA-AES256-SHA256 + DHE-RSA-AES128-SHA + DHE-RSA-AES256-SHA + DHE-DSS-AES128-SHA256 + DHE-DSS-AES256-SHA256 + DHE-DSS-AES128-SHA + DHE-DSS-AES256-SHA + AES128-GCM-SHA256 + AES256-GCM-SHA384 + AES128-SHA256 + AES256-SHA256 + AES128-SHA + AES256-SHA + ECDHE-ECDSA-RC4-SHA + ECDHE-RSA-RC4-SHA + RC4-SHA + }.join(":"), + :options => -> { + opts = OpenSSL::SSL::OP_ALL + opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS) + opts |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION) + opts |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2) + opts |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3) + opts + }.call + } + + DEFAULT_CERT_STORE = OpenSSL::X509::Store.new + DEFAULT_CERT_STORE.set_default_paths + if defined?(OpenSSL::X509::V_FLAG_CRL_CHECK_ALL) + DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL + end + + ## + # Sets the parameters for this SSL context to the values in +params+. + # The keys in +params+ must be assignment methods on SSLContext. + # + # If the verify_mode is not VERIFY_NONE and ca_file, ca_path and + # cert_store are not set then the system default certificate store is + # used. + + def set_params(params={}) + params = DEFAULT_PARAMS.merge(params) + params.each{|name, value| self.__send__("#{name}=", value) } + if self.verify_mode != OpenSSL::SSL::VERIFY_NONE + unless self.ca_file or self.ca_path or self.cert_store + self.cert_store = DEFAULT_CERT_STORE + end + end + return params + end + end + + module SocketForwarder + def addr + to_io.addr + end + + def peeraddr + to_io.peeraddr + end + + def setsockopt(level, optname, optval) + to_io.setsockopt(level, optname, optval) + end + + def getsockopt(level, optname) + to_io.getsockopt(level, optname) + end + + def fcntl(*args) + to_io.fcntl(*args) + end + + def closed? + to_io.closed? + end + + def do_not_reverse_lookup=(flag) + to_io.do_not_reverse_lookup = flag + end + end + + module Nonblock + def initialize(*args) + flag = File::NONBLOCK + flag |= @io.fcntl(Fcntl::F_GETFL) if defined?(Fcntl::F_GETFL) + @io.fcntl(Fcntl::F_SETFL, flag) + super + end + end + + def verify_certificate_identity(cert, hostname) + should_verify_common_name = true + cert.extensions.each{|ext| + next if ext.oid != "subjectAltName" + ostr = OpenSSL::ASN1.decode(ext.to_der).value.last + sequence = OpenSSL::ASN1.decode(ostr.value) + sequence.value.each{|san| + case san.tag + when 2 # dNSName in GeneralName (RFC5280) + should_verify_common_name = false + return true if verify_hostname(hostname, san.value) + when 7 # iPAddress in GeneralName (RFC5280) + should_verify_common_name = false + # follows GENERAL_NAME_print() in x509v3/v3_alt.c + if san.value.size == 4 + return true if san.value.unpack('C*').join('.') == hostname + elsif san.value.size == 16 + return true if san.value.unpack('n*').map { |e| sprintf("%X", e) }.join(':') == hostname + end + end + } + } + if should_verify_common_name + cert.subject.to_a.each{|oid, value| + if oid == "CN" + return true if verify_hostname(hostname, value) + end + } + end + return false + end + module_function :verify_certificate_identity + + def verify_hostname(hostname, san) # :nodoc: + # RFC 5280, IA5String is limited to the set of ASCII characters + return false unless san.ascii_only? + return false unless hostname.ascii_only? + + # See RFC 6125, section 6.4.1 + # Matching is case-insensitive. + san_parts = san.downcase.split(".") + + # TODO: this behavior should probably be more strict + return san == hostname if san_parts.size < 2 + + # Matching is case-insensitive. + host_parts = hostname.downcase.split(".") + + # RFC 6125, section 6.4.3, subitem 2. + # If the wildcard character is the only character of the left-most + # label in the presented identifier, the client SHOULD NOT compare + # against anything but the left-most label of the reference + # identifier (e.g., *.example.com would match foo.example.com but + # not bar.foo.example.com or example.com). + return false unless san_parts.size == host_parts.size + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in + # which the wildcard character comprises a label other than the + # left-most label (e.g., do not match bar.*.example.net). + return false unless verify_wildcard(host_parts.shift, san_parts.shift) + + san_parts.join(".") == host_parts.join(".") + end + module_function :verify_hostname + + def verify_wildcard(domain_component, san_component) # :nodoc: + parts = san_component.split("*", -1) + + return false if parts.size > 2 + return san_component == domain_component if parts.size == 1 + + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + return false if domain_component.start_with?("xn--") && san_component != "*" + + parts[0].length + parts[1].length < domain_component.length && + domain_component.start_with?(parts[0]) && + domain_component.end_with?(parts[1]) + end + module_function :verify_wildcard + + class SSLSocket + include Buffering + include SocketForwarder + include Nonblock + + ## + # Perform hostname verification after an SSL connection is established + # + # This method MUST be called after calling #connect to ensure that the + # hostname of a remote peer has been verified. + def post_connection_check(hostname) + if peer_cert.nil? + msg = "Peer verification enabled, but no certificate received." + if using_anon_cipher? + msg += " Anonymous cipher suite #{cipher[0]} was negotiated. Anonymous suites must be disabled to use peer verification." + end + raise SSLError, msg + end + + unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname) + raise SSLError, "hostname \"#{hostname}\" does not match the server certificate" + end + return true + end + + def session + SSL::Session.new(self) + rescue SSL::Session::SessionError + nil + end + + private + + def using_anon_cipher? + ctx = OpenSSL::SSL::SSLContext.new + ctx.ciphers = "aNULL" + ctx.ciphers.include?(cipher) + end + end + + ## + # SSLServer represents a TCP/IP server socket with Secure Sockets Layer. + class SSLServer + include SocketForwarder + # When true then #accept works exactly the same as TCPServer#accept + attr_accessor :start_immediately + + # Creates a new instance of SSLServer. + # * +srv+ is an instance of TCPServer. + # * +ctx+ is an instance of OpenSSL::SSL::SSLContext. + def initialize(svr, ctx) + @svr = svr + @ctx = ctx + unless ctx.session_id_context + # see #6137 - session id may not exceed 32 bytes + prng = ::Random.new($0.hash) + session_id = prng.bytes(16).unpack('H*')[0] + @ctx.session_id_context = session_id + end + @start_immediately = true + end + + # Returns the TCPServer passed to the SSLServer when initialized. + def to_io + @svr + end + + # See TCPServer#listen for details. + def listen(backlog=5) + @svr.listen(backlog) + end + + # See BasicSocket#shutdown for details. + def shutdown(how=Socket::SHUT_RDWR) + @svr.shutdown(how) + end + + # Works similar to TCPServer#accept. + def accept + # Socket#accept returns [socket, addrinfo]. + # TCPServer#accept returns a socket. + # The following comma strips addrinfo. + sock, = @svr.accept + begin + ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx) + ssl.sync_close = true + ssl.accept if @start_immediately + ssl + rescue Exception => ex + if ssl + ssl.close + else + sock.close + end + raise ex + end + end + + # See IO#close for details. + def close + @svr.close + end + end + end +end diff --git a/jni/ruby/ext/openssl/lib/openssl/x509.rb b/jni/ruby/ext/openssl/lib/openssl/x509.rb new file mode 100644 index 0000000..bdeadb1 --- /dev/null +++ b/jni/ruby/ext/openssl/lib/openssl/x509.rb @@ -0,0 +1,182 @@ +#-- +# +# $RCSfile$ +# +# = Ruby-space definitions that completes C-space funcs for X509 and subclasses +# +# = Info +# 'OpenSSL for Ruby 2' project +# Copyright (C) 2002 Michal Rokos +# All rights reserved. +# +# = Licence +# This program is licenced under the same licence as Ruby. +# (See the file 'LICENCE'.) +# +# = Version +# $Id: x509.rb 48521 2014-11-20 15:39:03Z usa $ +# +#++ + +module OpenSSL + module X509 + class ExtensionFactory + def create_extension(*arg) + if arg.size > 1 + create_ext(*arg) + else + send("create_ext_from_"+arg[0].class.name.downcase, arg[0]) + end + end + + def create_ext_from_array(ary) + raise ExtensionError, "unexpected array form" if ary.size > 3 + create_ext(ary[0], ary[1], ary[2]) + end + + def create_ext_from_string(str) # "oid = critical, value" + oid, value = str.split(/=/, 2) + oid.strip! + value.strip! + create_ext(oid, value) + end + + def create_ext_from_hash(hash) + create_ext(hash["oid"], hash["value"], hash["critical"]) + end + end + + class Extension + def to_s # "oid = critical, value" + str = self.oid + str << " = " + str << "critical, " if self.critical? + str << self.value.gsub(/\n/, ", ") + end + + def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false} + {"oid"=>self.oid,"value"=>self.value,"critical"=>self.critical?} + end + + def to_a + [ self.oid, self.value, self.critical? ] + end + end + + class Name + module RFC2253DN + Special = ',=+<>#;' + HexChar = /[0-9a-fA-F]/ + HexPair = /#{HexChar}#{HexChar}/ + HexString = /#{HexPair}+/ + Pair = /\\(?:[#{Special}]|\\|"|#{HexPair})/ + StringChar = /[^\\"#{Special}]/ + QuoteChar = /[^\\"]/ + AttributeType = /[a-zA-Z][0-9a-zA-Z]*|[0-9]+(?:\.[0-9]+)*/ + AttributeValue = / + (?!["#])((?:#{StringChar}|#{Pair})*)| + \#(#{HexString})| + "((?:#{QuoteChar}|#{Pair})*)" + /x + TypeAndValue = /\A(#{AttributeType})=#{AttributeValue}/ + + module_function + + def expand_pair(str) + return nil unless str + return str.gsub(Pair){ + pair = $& + case pair.size + when 2 then pair[1,1] + when 3 then Integer("0x#{pair[1,2]}").chr + else raise OpenSSL::X509::NameError, "invalid pair: #{str}" + end + } + end + + def expand_hexstring(str) + return nil unless str + der = str.gsub(HexPair){$&.to_i(16).chr } + a1 = OpenSSL::ASN1.decode(der) + return a1.value, a1.tag + end + + def expand_value(str1, str2, str3) + value = expand_pair(str1) + value, tag = expand_hexstring(str2) unless value + value = expand_pair(str3) unless value + return value, tag + end + + def scan(dn) + str = dn + ary = [] + while true + if md = TypeAndValue.match(str) + remain = md.post_match + type = md[1] + value, tag = expand_value(md[2], md[3], md[4]) rescue nil + if value + type_and_value = [type, value] + type_and_value.push(tag) if tag + ary.unshift(type_and_value) + if remain.length > 2 && remain[0] == ?, + str = remain[1..-1] + next + elsif remain.length > 2 && remain[0] == ?+ + raise OpenSSL::X509::NameError, + "multi-valued RDN is not supported: #{dn}" + elsif remain.empty? + break + end + end + end + msg_dn = dn[0, dn.length - str.length] + " =>" + str + raise OpenSSL::X509::NameError, "malformed RDN: #{msg_dn}" + end + return ary + end + end + + class << self + def parse_rfc2253(str, template=OBJECT_TYPE_TEMPLATE) + ary = OpenSSL::X509::Name::RFC2253DN.scan(str) + self.new(ary, template) + end + + def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE) + ary = str.scan(/\s*([^\/,]+)\s*/).collect{|i| i[0].split("=", 2) } + self.new(ary, template) + end + + alias parse parse_openssl + end + + def pretty_print(q) + q.object_group(self) { + q.text ' ' + q.text to_s(OpenSSL::X509::Name::RFC2253) + } + end + end + + class StoreContext + def cleanup + warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE + end + end + + class Certificate + def pretty_print(q) + q.object_group(self) { + q.breakable + q.text 'subject='; q.pp self.subject; q.text ','; q.breakable + q.text 'issuer='; q.pp self.issuer; q.text ','; q.breakable + q.text 'serial='; q.pp self.serial; q.text ','; q.breakable + q.text 'not_before='; q.pp self.not_before; q.text ','; q.breakable + q.text 'not_after='; q.pp self.not_after + } + end + end + end +end -- cgit v1.2.3-70-g09d2