Initial Commit

This commit is contained in:
G. Gibson 2021-11-16 08:58:51 -08:00
commit 81daf6f387
283 changed files with 130672 additions and 1 deletions

View file

@ -0,0 +1,40 @@
# frozen_string_literal: false
#--
#
# = Ruby-space definitions that completes C-space funcs for BN
#
# = Info
# 'OpenSSL for Ruby 2' project
# Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
# All rights reserved.
#
# = Licence
# This program is licensed under the same licence as Ruby.
# (See the file 'LICENCE'.)
#++
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

View file

@ -0,0 +1,455 @@
# coding: binary
# frozen_string_literal: false
#--
#= Info
# 'OpenSSL for Ruby 2' project
# Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
# All rights reserved.
#
#= Licence
# This program is licensed under the same licence as Ruby.
# (See the file 'LICENCE'.)
#++
##
# 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
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
#
# By specifying a keyword argument _exception_ to +false+, you can indicate
# that read_nonblock should not raise an IO::Wait*able exception, but
# return the symbol +:wait_writable+ or +:wait_readable+ instead. At EOF,
# it will return +nil+ instead of raising EOFError.
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
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 size && limit && 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 @wbuffer.rindex("\n")
until @wbuffer.empty?
begin
nwrote = syswrite(@wbuffer)
rescue Errno::EAGAIN
retry
end
@wbuffer[0, nwrote] = ""
end
end
end
public
##
# Writes _s_ to the stream. If the argument is not a String it will be
# converted using +.to_s+ method. Returns the number of bytes written.
def write(*s)
s.inject(0) do |written, str|
do_write(str)
written + str.bytesize
end
end
##
# Writes _s_ 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
#
# By specifying a keyword argument _exception_ to +false+, you can indicate
# that write_nonblock should not raise an IO::Wait*able exception, but
# return the symbol +:wait_writable+ or +:wait_readable+ instead.
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
# +.to_s+ method.
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
s.sub!(/(?<!\n)\z/, "\n")
}
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

View file

@ -0,0 +1,67 @@
# frozen_string_literal: false
#--
# = Ruby-space predefined Cipher subclasses
#
# = Info
# 'OpenSSL for Ruby 2' project
# Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
# All rights reserved.
#
# = Licence
# This program is licensed under the same licence as Ruby.
# (See the file 'LICENCE'.)
#++
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.downcase)
}
}
const_set(name, klass)
}
%w(128 192 256).each{|keylen|
klass = Class.new(Cipher){
define_method(:initialize){|mode = "CBC"|
super("aes-#{keylen}-#{mode}".downcase)
}
}
const_set("AES#{keylen}", klass)
}
# call-seq:
# cipher.random_key -> key
#
# Generate a random key with OpenSSL::Random.random_bytes and sets it to
# the cipher, and returns it.
#
# You must call #encrypt or #decrypt before calling this method.
def random_key
str = OpenSSL::Random.random_bytes(self.key_len)
self.key = str
end
# call-seq:
# cipher.random_iv -> iv
#
# Generate a random IV with OpenSSL::Random.random_bytes and sets it to the
# cipher, and returns it.
#
# You must call #encrypt or #decrypt before calling this method.
def random_iv
str = OpenSSL::Random.random_bytes(self.iv_len)
self.iv = str
end
# Deprecated.
#
# This class is only provided for backwards compatibility.
# Use OpenSSL::Cipher.
class Cipher < Cipher; end
deprecate_constant :Cipher
end # Cipher
end # OpenSSL

View file

@ -0,0 +1,474 @@
# frozen_string_literal: false
=begin
= Ruby-space definitions that completes C-space funcs for Config
= Info
Copyright (C) 2010 Hiroshi Nakamura <nahi@ruby-lang.org>
= Licence
This program is licensed 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')
# #=> #<OpenSSL::Config sections=["default"]>
# 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')
# #=> #<OpenSSL::Config sections=["default"]>
# 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')
# #=> #<OpenSSL::Config sections=["default"]>
# 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
# #=> #<OpenSSL::Config sections=[]>
# 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
# #=> #<OpenSSL::Config sections=[]>
# 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)
# #=> #<OpenSSL::Config sections=["default"]>
# 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

View file

@ -0,0 +1,75 @@
# frozen_string_literal: false
#--
# = Ruby-space predefined Digest subclasses
#
# = Info
# 'OpenSSL for Ruby 2' project
# Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
# All rights reserved.
#
# = Licence
# This program is licensed under the same licence as Ruby.
# (See the file 'LICENCE'.)
#++
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)
end
# Return the hash value 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.
# Use OpenSSL::Digest instead.
class Digest < Digest; end # :nodoc:
deprecate_constant :Digest
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

View file

@ -0,0 +1,22 @@
# frozen_string_literal: false
#--
# Ruby/OpenSSL Project
# Copyright (C) 2017 Ruby/OpenSSL Project Authors
#++
module OpenSSL
module PKCS5
module_function
# OpenSSL::PKCS5.pbkdf2_hmac has been renamed to OpenSSL::KDF.pbkdf2_hmac.
# This method is provided for backwards compatibility.
def pbkdf2_hmac(pass, salt, iter, keylen, digest)
OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
length: keylen, hash: digest)
end
def pbkdf2_hmac_sha1(pass, salt, iter, keylen)
pbkdf2_hmac(pass, salt, iter, keylen, "sha1")
end
end
end

View file

@ -0,0 +1,25 @@
# frozen_string_literal: false
#--
# Ruby/OpenSSL Project
# Copyright (C) 2017 Ruby/OpenSSL Project Authors
#++
module OpenSSL::PKey
if defined?(EC)
class EC::Point
# :call-seq:
# point.to_bn([conversion_form]) -> OpenSSL::BN
#
# Returns the octet string representation of the EC point as an instance of
# OpenSSL::BN.
#
# If _conversion_form_ is not given, the _point_conversion_form_ attribute
# set to the group is used.
#
# See #to_octet_string for more information.
def to_bn(conversion_form = group.point_conversion_form)
OpenSSL::BN.new(to_octet_string(conversion_form), 2)
end
end
end
end

504
gxg/standard/openssl/ssl.rb Normal file
View file

@ -0,0 +1,504 @@
# frozen_string_literal: false
=begin
= Info
'OpenSSL for Ruby 2' project
Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
All rights reserved.
= Licence
This program is licensed under the same licence as Ruby.
(See the file 'LICENCE'.)
=end
require "openssl/buffering"
require "io/nonblock"
require "ipaddr"
module OpenSSL
module SSL
class SSLContext
DEFAULT_PARAMS = { # :nodoc:
:min_version => OpenSSL::SSL::TLS1_VERSION,
:verify_mode => OpenSSL::SSL::VERIFY_PEER,
:verify_hostname => true,
:options => -> {
opts = OpenSSL::SSL::OP_ALL
opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS
opts |= OpenSSL::SSL::OP_NO_COMPRESSION
opts
}.call
}
if defined?(OpenSSL::PKey::DH)
DEFAULT_2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY
JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab
VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6
YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD
7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg==
-----END DH PARAMETERS-----
_end_of_pem_
private_constant :DEFAULT_2048
DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| # :nodoc:
warn "using default DH parameters." if $VERBOSE
DEFAULT_2048
}
end
if !(OpenSSL::OPENSSL_VERSION.start_with?("OpenSSL") &&
OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10100000)
DEFAULT_PARAMS.merge!(
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
}.join(":"),
)
end
DEFAULT_CERT_STORE = OpenSSL::X509::Store.new # :nodoc:
DEFAULT_CERT_STORE.set_default_paths
DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
# A callback invoked when DH parameters are required.
#
# The callback is invoked with the Session for the key exchange, an
# flag indicating the use of an export cipher and the keylength
# required.
#
# The callback must return an OpenSSL::PKey::DH instance of the correct
# key length.
attr_accessor :tmp_dh_callback
# A callback invoked at connect time to distinguish between multiple
# server names.
#
# The callback is invoked with an SSLSocket and a server name. The
# callback must return an SSLContext for the server name or nil.
attr_accessor :servername_cb
# call-seq:
# SSLContext.new -> ctx
# SSLContext.new(:TLSv1) -> ctx
# SSLContext.new("SSLv23") -> ctx
#
# Creates a new SSL context.
#
# If an argument is given, #ssl_version= is called with the value. Note
# that this form is deprecated. New applications should use #min_version=
# and #max_version= as necessary.
def initialize(version = nil)
self.options |= OpenSSL::SSL::OP_ALL
self.ssl_version = version if version
end
##
# call-seq:
# ctx.set_params(params = {}) -> params
#
# Sets saner defaults optimized for the use with HTTP-like protocols.
#
# If a Hash _params_ is given, the parameters are overridden with it.
# 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)
self.options = params.delete(:options) # set before min_version/max_version
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
# call-seq:
# ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
# ctx.min_version = :TLS1_2
# ctx.min_version = nil
#
# Sets the lower bound on the supported SSL/TLS protocol version. The
# version may be specified by an integer constant named
# OpenSSL::SSL::*_VERSION, a Symbol, or +nil+ which means "any version".
#
# Be careful that you don't overwrite OpenSSL::SSL::OP_NO_{SSL,TLS}v*
# options by #options= once you have called #min_version= or
# #max_version=.
#
# === Example
# ctx = OpenSSL::SSL::SSLContext.new
# ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION
# ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
#
# sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx)
# sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2
def min_version=(version)
set_minmax_proto_version(version, @max_proto_version ||= nil)
@min_proto_version = version
end
# call-seq:
# ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
# ctx.max_version = :TLS1_2
# ctx.max_version = nil
#
# Sets the upper bound of the supported SSL/TLS protocol version. See
# #min_version= for the possible values.
def max_version=(version)
set_minmax_proto_version(@min_proto_version ||= nil, version)
@max_proto_version = version
end
# call-seq:
# ctx.ssl_version = :TLSv1
# ctx.ssl_version = "SSLv23"
#
# Sets the SSL/TLS protocol version for the context. This forces
# connections to use only the specified protocol version. This is
# deprecated and only provided for backwards compatibility. Use
# #min_version= and #max_version= instead.
#
# === History
# As the name hints, this used to call the SSL_CTX_set_ssl_version()
# function which sets the SSL method used for connections created from
# the context. As of Ruby/OpenSSL 2.1, this accessor method is
# implemented to call #min_version= and #max_version= instead.
def ssl_version=(meth)
meth = meth.to_s if meth.is_a?(Symbol)
if /(?<type>_client|_server)\z/ =~ meth
meth = $`
if $VERBOSE
warn "#{caller(1, 1)[0]}: method type #{type.inspect} is ignored"
end
end
version = METHODS_MAP[meth.intern] or
raise ArgumentError, "unknown SSL method `%s'" % meth
set_minmax_proto_version(version, version)
@min_proto_version = @max_proto_version = version
end
METHODS_MAP = {
SSLv23: 0,
SSLv2: OpenSSL::SSL::SSL2_VERSION,
SSLv3: OpenSSL::SSL::SSL3_VERSION,
TLSv1: OpenSSL::SSL::TLS1_VERSION,
TLSv1_1: OpenSSL::SSL::TLS1_1_VERSION,
TLSv1_2: OpenSSL::SSL::TLS1_2_VERSION,
}.freeze
private_constant :METHODS_MAP
# The list of available SSL/TLS methods. This constant is only provided
# for backwards compatibility.
METHODS = METHODS_MAP.flat_map { |name,|
[name, :"#{name}_client", :"#{name}_server"]
}.freeze
deprecate_constant :METHODS
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
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
if san.value.size == 4 || san.value.size == 16
begin
return true if san.value == IPAddr.new(hostname).hton
rescue IPAddr::InvalidAddressError
end
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
attr_reader :hostname
# The underlying IO object.
attr_reader :io
alias :to_io :io
# The SSLContext object used in this connection.
attr_reader :context
# Whether to close the underlying socket as well, when the SSL/TLS
# connection is shut down. This defaults to +false+.
attr_accessor :sync_close
# call-seq:
# ssl.sysclose => nil
#
# Sends "close notify" to the peer and tries to shut down the SSL
# connection gracefully.
#
# If sync_close is set to +true+, the underlying IO is also closed.
def sysclose
return if closed?
stop
io.close if sync_close
end
# call-seq:
# ssl.post_connection_check(hostname) -> true
#
# Perform hostname verification following RFC 6125.
#
# 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
# call-seq:
# ssl.session -> aSession
#
# Returns the SSLSession object currently used, or nil if the session is
# not established.
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
def client_cert_cb
@context.client_cert_cb
end
def tmp_dh_callback
@context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK
end
def tmp_ecdh_callback
@context.tmp_ecdh_callback
end
def session_new_cb
@context.session_new_cb
end
def session_get_cb
@context.session_get_cb
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

View file

@ -0,0 +1,215 @@
# frozen_string_literal: false
#--
# = Ruby-space definitions that completes C-space funcs for X509 and subclasses
#
# = Info
# 'OpenSSL for Ruby 2' project
# Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
# All rights reserved.
#
# = Licence
# This program is licensed under the same licence as Ruby.
# (See the file 'LICENCE'.)
#++
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 ==(other)
return false unless Extension === other
to_der == other.to_der
end
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)
if str.start_with?("/")
# /A=B/C=D format
ary = str[1..-1].split("/").map { |i| i.split("=", 2) }
else
# Comma-separated
ary = str.split(",").map { |i| i.strip.split("=", 2) }
end
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 Attribute
def ==(other)
return false unless Attribute === other
to_der == other.to_der
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
class CRL
def ==(other)
return false unless CRL === other
to_der == other.to_der
end
end
class Revoked
def ==(other)
return false unless Revoked === other
to_der == other.to_der
end
end
class Request
def ==(other)
return false unless Request === other
to_der == other.to_der
end
end
end
end