pax_global_header00006660000000000000000000000064133615704500014516gustar00rootroot0000000000000052 comment=1b7e5e49265a88faa30791f8d677ae0a3b4c54f6 openssl-2.0.9/000077500000000000000000000000001336157045000132115ustar00rootroot00000000000000openssl-2.0.9/.gitignore000066400000000000000000000001531336157045000152000ustar00rootroot00000000000000/doc/ /pkg/ /tmp/ /html/ *.bundle *.so *.o ext/openssl/mkmf.log ext/openssl/Makefile ext/openssl/extconf.h openssl-2.0.9/.travis.yml000066400000000000000000000027701336157045000153300ustar00rootroot00000000000000language: c sudo: required group: edge dist: trusty services: - docker before_install: - sudo apt-get -qq update install: - sudo sh -c "curl -L https://github.com/docker/compose/releases/download/1.8.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose" - sudo chmod +x /usr/local/bin/docker-compose script: - docker -v - docker-compose -v - docker-compose build --no-cache - docker-compose run test matrix: fast_finish: true include: - env: RUBY_VERSION=ruby-2.3 OPENSSL_VERSION=openssl-1.0.2 - env: RUBY_VERSION=ruby-2.4 OPENSSL_VERSION=openssl-1.0.2 - env: RUBY_VERSION=ruby-2.5 OPENSSL_VERSION=openssl-1.0.0 - env: RUBY_VERSION=ruby-2.5 OPENSSL_VERSION=openssl-1.0.1 - env: RUBY_VERSION=ruby-2.5 OPENSSL_VERSION=openssl-1.0.2 - env: RUBY_VERSION=ruby-2.5 OPENSSL_VERSION=openssl-1.1.0 - env: RUBY_VERSION=ruby-2.5 OPENSSL_VERSION=openssl-1.1.1 - env: RUBY_VERSION=ruby-2.5 OPENSSL_VERSION=libressl-2.3 - env: RUBY_VERSION=ruby-2.5 OPENSSL_VERSION=libressl-2.4 - env: RUBY_VERSION=ruby-2.5 OPENSSL_VERSION=libressl-2.5 - env: RUBY_VERSION=ruby-2.5 OPENSSL_VERSION=libressl-2.6 - env: RUBY_VERSION=ruby-2.5 OPENSSL_VERSION=libressl-2.7 - language: ruby rvm: ruby-head before_install: - "rake install_dependencies" script: - "rake compile -- --enable-debug" - "rake test" allow_failures: - language: ruby rvm: ruby-head - env: RUBY_VERSION=ruby-2.5 OPENSSL_VERSION=openssl-1.1.1 openssl-2.0.9/BSDL000066400000000000000000000024021336157045000136560ustar00rootroot00000000000000Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. openssl-2.0.9/CONTRIBUTING.md000066400000000000000000000112151336157045000154420ustar00rootroot00000000000000# Contributing to Ruby OpenSSL Thank you for your interest in contributing to Ruby OpenSSL! This documentation provides an overview how you can contribute. ## Bugs and feature requests Bugs and feature requests are tracked on [GitHub]. If you think you found a bug, file a ticket on GitHub. Please DO NOT report security issues here, there is a separate procedure which is described on ["Security at ruby-lang.org"](https://www.ruby-lang.org/en/security/). When reporting a bug, please make sure you include the version of Ruby, the version of openssl gem, the version of the OpenSSL library, along with a sample file that illustrates the problem or link to repository or gem that is associated with the bug. There is a number of unresolved issues and feature requests for openssl that need review. Before submitting a new ticket, it is recommended to check [known issues] and [bugs.ruby-lang.org], the previous issue tracker. ## Submitting patches Patches are also very welcome! Please submit a [pull request] with your changes. Make sure that your branch does: * Have good commit messages * Follow Ruby's coding style ([DeveloperHowTo]) * Pass the test suite successfully (see "Testing") * Add an entry to [History.md] if necessary ## Testing We have a test suite! Test cases are located under the [`test/`](https://github.com/ruby/openssl/tree/master/test) directory. You can run it with the following three commands: ``` $ rake install_dependencies # installs rake-compiler, test-unit, ... $ rake compile $ rake test ``` ### Docker You can also use Docker Compose to run tests. It can be used to check that your changes work correctly with various supported versions of Ruby and OpenSSL. First, you need to install [Docker](https://www.docker.com/products/docker) and [Docker Compose](https://www.docker.com/products/docker-compose) on your computer. If you're on MacOS or Windows, we recommended to use the official [Docker Toolbox](https://www.docker.com/products/docker-toolbox). On Linux, follow the instructions for your package manager. For further information, please check the [official documentation](https://docs.docker.com/). Once you have Docker and Docker Compose, running the following commands will build the container and execute the openssl tests. In this example, we will use Ruby version 2.3 with OpenSSL version 1.0.2. ``` $ docker-compose build $ export RUBY_VERSION=ruby-2.3 $ export OPENSSL_VERSION=openssl-1.0.2 $ docker-compose run test # You may want an interactive shell for dubugging $ docker-compose run debug ``` All possible values for `RUBY_VERSION` and `OPENSSL_VERSION` can be found in [`.travis.yml`](https://github.com/ruby/openssl/tree/master/.travis.yml). **NOTE**: these commands must be run from the openssl repository root, in order to use the [`docker-compose.yml`](https://github.com/ruby/openssl/blob/master/docker-compose.yml) file we have provided. This Docker image is built using the [Dockerfile](https://github.com/ruby/openssl/tree/master/tool/ruby-openssl-docker) provided in the repository. ## Relation with Ruby source tree After Ruby 2.3, `ext/openssl` was converted into a "default gem", a library which ships with standard Ruby builds but can be upgraded via RubyGems. This means the development of this gem has migrated to a [separate repository][GitHub] and will be released independently. The version included in the Ruby source tree (trunk branch) is synchronized with the latest release. ## Release policy Bug fixes (including security fixes) will be made only for the version series included in a stable Ruby release. ## Security If you discovered a security issue, please send us in private, using the security issue handling procedure for Ruby core. You can either use [HackerOne] or send an email to security@ruby-lang.org. Please see [Security] page on ruby-lang.org website for details. Reported problems will be published after a fix is released. _Thanks for your contributions!_ _\- The Ruby OpenSSL team_ [GitHub]: https://github.com/ruby/openssl [known issues]: https://github.com/ruby/openssl/issues [bugs.ruby-lang.org]: https://bugs.ruby-lang.org/issues?utf8=%E2%9C%93&set_filter=1&f%5B%5D=status_id&op%5Bstatus_id%5D=o&f%5B%5D=assigned_to_id&op%5Bassigned_to_id%5D=%3D&v%5Bassigned_to_id%5D%5B%5D=7150&f%5B%5D=&c%5B%5D=project&c%5B%5D=tracker&c%5B%5D=status&c%5B%5D=subject&c%5B%5D=assigned_to&c%5B%5D=updated_on&group_by=&t%5B%5D= [DeveloperHowTo]: https://bugs.ruby-lang.org/projects/ruby/wiki/DeveloperHowto [HackerOne]: https://hackerone.com/ruby [Security]: https://www.ruby-lang.org/en/security/ [pull request]: https://github.com/ruby/openssl/compare [History.md]: https://github.com/ruby/openssl/tree/master/History.md openssl-2.0.9/Dockerfile000066400000000000000000000000461336157045000152030ustar00rootroot00000000000000FROM zzak/ruby-openssl-docker:testing openssl-2.0.9/History.md000066400000000000000000000215531336157045000152020ustar00rootroot00000000000000Version 2.0.9 ============= Security fixes -------------- * OpenSSL::X509::Name#<=> could incorrectly return 0 (= equal) for non-equal objects. CVE-2018-16395 is assigned for this issue. https://hackerone.com/reports/387250 Bug fixes --------- * Fixed OpenSSL::PKey::*.{new,generate} immediately aborting if the thread is interrupted. [[Bug #14882]](https://bugs.ruby-lang.org/issues/14882) [[GitHub #205]](https://github.com/ruby/openssl/pull/205) * Fixed OpenSSL::X509::Name#to_s failing with OpenSSL::X509::NameError if called against an empty instance. [[GitHub #200]](https://github.com/ruby/openssl/issues/200) [[GitHub #211]](https://github.com/ruby/openssl/pull/211) Version 2.0.8 ============= Bug fixes --------- * OpenSSL::Cipher#pkcs5_keyivgen raises an error when a negative iteration count is given. [[GitHub #184]](https://github.com/ruby/openssl/pull/184) * Fixed build with LibreSSL 2.7. [[GitHub #192]](https://github.com/ruby/openssl/issues/192) [[GitHub #193]](https://github.com/ruby/openssl/pull/193) Version 2.0.7 ============= Bug fixes --------- * OpenSSL::Cipher#auth_data= could segfault if called against a non-AEAD cipher. [[Bug #14024]](https://bugs.ruby-lang.org/issues/14024) * OpenSSL::X509::Certificate#public_key= (and similar methods) could segfault when an instance of OpenSSL::PKey::PKey with no public key components is passed. [[Bug #14087]](https://bugs.ruby-lang.org/issues/14087) [[GitHub #168]](https://github.com/ruby/openssl/pull/168) Version 2.0.6 ============= Bug fixes --------- * The session_remove_cb set to an OpenSSL::SSL::SSLContext is no longer called during GC. * A possible deadlock in OpenSSL::SSL::SSLSocket#sysread is fixed. [[GitHub #139]](https://github.com/ruby/openssl/pull/139) * OpenSSL::BN#hash could return an unnormalized fixnum value on Windows. [[Bug #13877]](https://bugs.ruby-lang.org/issues/13877) * OpenSSL::SSL::SSLSocket#sysread and #sysread_nonblock set the length of the destination buffer String to 0 on error. [[GitHub #153]](https://github.com/ruby/openssl/pull/153) * Possible deadlock is fixed. This happened only when built with older versions of OpenSSL (before 1.1.0) or LibreSSL. [[GitHub #155]](https://github.com/ruby/openssl/pull/155) Version 2.0.5 ============= Bug fixes --------- * Reading a PEM/DER-encoded private key or certificate from an IO object did not work properly on mswin platforms. [[ruby/openssl#128]](https://github.com/ruby/openssl/issues/128) * Broken length check in the PEM passphrase callback is fixed. * It failed to compile when OpenSSL is configured without TLS 1.0 support. Version 2.0.4 ============= Bug fixes --------- * It now compiles with LibreSSL without renaming on Windows (mswin). * A workaround for the error queue leak of X509_load_cert_crl_file() that causes random errors is added. [[Bug #11033]](https://bugs.ruby-lang.org/issues/11033) Version 2.0.3 ============= Bug fixes --------- * OpenSSL::ASN1::Constructive#each which was broken by 2.0.0 is fixed. [[ruby/openssl#96]](https://github.com/ruby/openssl/pull/96) * Fixed build with static OpenSSL libraries on Windows. [[Bug #13080]](https://bugs.ruby-lang.org/issues/13080) * OpenSSL::X509::Name#eql? which was broken by 2.0.0 is fixed. Version 2.0.2 ============= Bug fixes --------- * Fix build with early 0.9.8 series which did not have SSL_CTX_clear_options(). [ruby-core:78693] Version 2.0.1 ============= Bug fixes --------- * A GC issue around OpenSSL::BN is fixed. [[ruby/openssl#87]](https://github.com/ruby/openssl/issues/87) * OpenSSL::ASN1 now parses BER encoding of GeneralizedTime without seconds. [[ruby/openssl#88]](https://github.com/ruby/openssl/pull/88) Version 2.0.0 ============= This is the first release of openssl gem, formerly a standard library of Ruby, ext/openssl. This is the successor of the version included in Ruby 2.3. Compatibility notes ------------------- * Support for OpenSSL version 0.9.6 and 0.9.7 is completely removed. openssl gem still works with OpenSSL 0.9.8, but users are strongly encouraged to upgrade to at least 1.0.1, as OpenSSL < 1.0.1 will not receive any security fixes from the OpenSSL development team. Supported platforms ------------------- * OpenSSL 1.0.0, 1.0.1, 1.0.2, 1.1.0 * OpenSSL < 0.9.8 is no longer supported. * LibreSSL 2.3, 2.4, 2.5 * Ruby 2.3, 2.4 Notable changes --------------- * Add support for OpenSSL 1.1.0. [[Feature #12324]](https://bugs.ruby-lang.org/issues/12324) * Add support for LibreSSL * OpenSSL::Cipher - OpenSSL::Cipher#key= and #iv= reject too long inputs. They used to truncate silently. [[Bug #12561]](https://bugs.ruby-lang.org/issues/12561) - OpenSSL::Cipher#iv_len= is added. It allows changing IV (nonce) length if using AEAD ciphers. [[Bug #8667]](https://bugs.ruby-lang.org/issues/8667), [[Bug #10420]](https://bugs.ruby-lang.org/issues/10420), [[GH ruby/ruby#569]](https://github.com/ruby/ruby/pull/569), [[GH ruby/openssl#58]](https://github.com/ruby/openssl/pull/58) - OpenSSL::Cipher#auth_tag_len= is added. This sets the authentication tag length to be generated by an AEAD cipher. * OpenSSL::OCSP - Accessor methods are added to OpenSSL::OCSP::CertificateId. [[Feature #7181]](https://bugs.ruby-lang.org/issues/7181) - OpenSSL::OCSP::Request and BasicResponse can be signed with non-SHA-1 hash algorithm. [[Feature #11552]](https://bugs.ruby-lang.org/issues/11552) - OpenSSL::OCSP::CertificateId and BasicResponse can be encoded into DER. - A new class OpenSSL::OCSP::SingleResponse is added for convenience. - OpenSSL::OCSP::BasicResponse#add_status accepts absolute times. They used to accept only relative seconds from the current time. * OpenSSL::PKey - OpenSSL::PKey::EC follows the general PKey interface. [[Bug #6567]](https://bugs.ruby-lang.org/issues/6567) - OpenSSL::PKey.read raises OpenSSL::PKey::PKeyError instead of ArgumentError for consistency with OpenSSL::PKey::{DH,DSA,RSA,EC}#new. [[Bug #11774]](https://bugs.ruby-lang.org/issues/11774), [[GH ruby/openssl#55]](https://github.com/ruby/openssl/pull/55) - OpenSSL::PKey::EC::Group retrieved by OpenSSL::PKey::EC#group is no longer linked with the EC key. Modifications to the EC::Group have no effect on the key. [[GH ruby/openssl#71]](https://github.com/ruby/openssl/pull/71) - OpenSSL::PKey::EC::Point#to_bn allows specifying the point conversion form by the optional argument. * OpenSSL::SSL - OpenSSL::SSL::SSLSocket#tmp_key is added. A client can call it after the connection is established to retrieve the ephemeral key. [[GH ruby/ruby#1318]](https://github.com/ruby/ruby/pull/1318) - The automatic ephemeral ECDH curve selection is enabled by default when built with OpenSSL >= 1.0.2 or LibreSSL. - OpenSSL::SSL::SSLContext#security_level= is added. You can set the "security level" of the SSL context. This is effective only when built with OpenSSL 1.1.0. - A new option 'verify_hostname' is added to OpenSSL::SSL::SSLContext. When it is enabled, and the SNI hostname is also set, the hostname verification on the server certificate is automatically performed. It is now enabled by OpenSSL::SSL::SSLContext#set_params. [[GH ruby/openssl#60]](https://github.com/ruby/openssl/pull/60) Removals -------- * OpenSSL::Engine - OpenSSL::Engine.cleanup does nothing when built with OpenSSL 1.1.0. * OpenSSL::SSL - OpenSSL::PKey::DH::DEFAULT_512 is removed. Hence servers no longer use 512-bit DH group by default. It is considered too weak nowadays. [[Bug #11968]](https://bugs.ruby-lang.org/issues/11968), [[GH ruby/ruby#1196]](https://github.com/ruby/ruby/pull/1196) - RC4 cipher suites are removed from OpenSSL::SSL::SSLContext::DEFAULT_PARAMS. RC4 is now considered to be weak. [[GH ruby/openssl#50]](https://github.com/ruby/openssl/pull/50) Deprecations ------------ * OpenSSL::PKey - OpenSSL::PKey::RSA#n=, #e=, #d=, #p=, #q=, #dmp1=, #dmq1=, #iqmp=, OpenSSL::PKey::DSA#p=, #q=, #g=, #priv_key=, #pub_key=, OpenSSL::PKey::DH#p=, #g=, #priv_key= and #pub_key= are deprecated. They are disabled when built with OpenSSL 1.1.0, due to its API change. Instead, OpenSSL::PKey::RSA#set_key, #set_factors, #set_crt_params, OpenSSL::PKey::DSA#set_pqg, #set_key, OpenSSL::PKey::DH#set_pqg and #set_key are added. * OpenSSL::Random - OpenSSL::Random.pseudo_bytes is deprecated, and not defined when built with OpenSSL 1.1.0. Use OpenSSL::Random.random_bytes instead. * OpenSSL::SSL - OpenSSL::SSL::SSLContext#tmp_ecdh_callback is deprecated, as the underlying API SSL_CTX_set_tmp_ecdh_callback() is removed in OpenSSL 1.1.0. It was first added in Ruby 2.3.0. To specify the curve to be used in ephemeral ECDH, use OpenSSL::SSL::SSLContext#ecdh_curves=. The automatic curve selection is also now enabled by default when built with a capable OpenSSL. openssl-2.0.9/LICENSE.txt000066400000000000000000000047061336157045000150430ustar00rootroot00000000000000Ruby is copyrighted free software by Yukihiro Matsumoto . You can redistribute it and/or modify it under either the terms of the 2-clause BSDL (see the file BSDL), or the conditions below: 1. You may make and give away verbatim copies of the source form of the software without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may modify your copy of the software in any way, provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software. b) use the modified software only within your corporation or organization. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 3. You may distribute the software in object code or binary form, provided that you do at least ONE of the following: a) distribute the binaries and library files of the software, together with instructions (in the manual page or equivalent) on where to get the original distribution. b) accompany the distribution with the machine-readable source of the software. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 4. You may modify and include the part of the software into any other software (possibly commercial). But some files in the distribution are not written by the author, so that they are not under these terms. For the list of those files and their copying conditions, see the file LEGAL. 5. The scripts and library files supplied as input to or produced as output from the software do not automatically fall under the copyright of the software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this software. 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. openssl-2.0.9/README.md000066400000000000000000000031061336157045000144700ustar00rootroot00000000000000# OpenSSL for Ruby [![Build Status](https://travis-ci.org/ruby/openssl.svg?branch=master)](https://travis-ci.org/ruby/openssl) [![Build status](https://ci.appveyor.com/api/projects/status/b8djtmwo7l26f88y/branch/master?svg=true)](https://ci.appveyor.com/project/ruby/openssl/branch/master) OpenSSL provides SSL, TLS and general purpose cryptography. It wraps the OpenSSL library. ## Installation The openssl gem is available at [rubygems.org](https://rubygems.org/gems/openssl). You can install with: ``` gem install openssl ``` You may need to specify the path where OpenSSL is installed. ``` gem install openssl -- --with-openssl-dir=/opt/openssl ``` Alternatively, you can install the gem with `bundler`: ```ruby # Gemfile gem 'openssl' # or specify git master gem 'openssl', github: 'ruby/openssl' ``` After doing `bundle install`, you should have the gem installed in your bundle. ## Usage Once installed, you can require "openssl" in your application. ```ruby require "openssl" ``` **NOTE**: If you are using Ruby 2.3 (and not Bundler), you **must** activate the gem version of openssl, otherwise the default gem packaged with the Ruby installation will be used: ```ruby gem "openssl" require "openssl" ``` ## Documentation See https://ruby.github.io/openssl/. ## Contributing Please read our [CONTRIBUTING.md] for instructions. ## Security Security issues should be reported to ruby-core by following the process described on ["Security at ruby-lang.org"](https://www.ruby-lang.org/en/security/). [CONTRIBUTING.md]: https://github.com/ruby/openssl/tree/master/CONTRIBUTING.md openssl-2.0.9/Rakefile000066400000000000000000000042171336157045000146620ustar00rootroot00000000000000require 'rake' require 'rake/testtask' require 'rdoc/task' begin require 'rake/extensiontask' Rake::ExtensionTask.new('openssl') rescue LoadError warn "rake-compiler not installed. Run 'rake install_dependencies' to " \ "install testing dependency gems." end Rake::TestTask.new do |t| t.libs << 'test' t.warning = true end RDoc::Task.new do |rdoc| rdoc.main = "README.md" rdoc.rdoc_files.include("*.md", "lib/**/*.rb", "ext/**/*.c") end task :test => :debug task :debug do ruby "-I./lib -ropenssl -ve'puts OpenSSL::OPENSSL_VERSION, OpenSSL::OPENSSL_LIBRARY_VERSION'" end task :install_dependencies do if ENV["USE_HTTP_RUBYGEMS_ORG"] == "1" Gem.sources.replace([Gem::Source.new("http://rubygems.org")]) end Gem.configuration.verbose = false gemspec = eval(File.read("openssl.gemspec")) gemspec.development_dependencies.each do |dep| print "Installing #{dep.name} (#{dep.requirement}) ... " installed = dep.matching_specs if installed.empty? installed = Gem.install(dep.name, dep.requirement) puts "#{installed[0].version}" else puts "(found #{installed[0].version})" end end end namespace :sync do task :from_ruby do sh "./tool/sync-with-trunk" end task :to_ruby do trunk_path = ENV.fetch("RUBY_TRUNK_PATH", "../ruby") rsync = "rsync -av --delete" excludes = %w{Makefile extconf.h mkmf.log depend *.o *.so *.bundle} excludes.each { |name| rsync << " --exclude #{name}" } paths = [ ["ext/openssl/", "ext/openssl/"], ["lib/", "ext/openssl/lib/"], ["sample/", "sample/openssl/"], ["test/fixtures/", "test/openssl/fixtures/"], ["test/utils.rb", "test/openssl/"], ["test/ut_eof.rb", "test/openssl/"], ["test/test_*", "test/openssl/"], ["History.md", "ext/openssl/"], ] paths.each do |src, dst| sh "#{rsync} #{src} #{trunk_path}/#{dst}" end gemspec_file = File.expand_path("../openssl.gemspec", __FILE__) gemspec = eval(File.read(gemspec_file), binding, gemspec_file) File.write("#{trunk_path}/ext/openssl/openssl.gemspec", gemspec.to_ruby) puts "Don't forget to update ext/openssl/depend" end end openssl-2.0.9/appveyor.yml000066400000000000000000000016521336157045000156050ustar00rootroot00000000000000--- clone_depth: 10 install: - ps: | $Env:PATH = "C:\Ruby${Env:ruby_version}\bin;${Env:PATH}" if ($Env:ruby_version -match "^23" ) { # RubyInstaller; download OpenSSL headers from OpenKnapsack Project $Env:openssl_dir = "C:\Ruby${Env:ruby_version}" appveyor DownloadFile http://dl.bintray.com/oneclick/OpenKnapsack/x64/openssl-1.0.2j-x64-windows.tar.lzma 7z e openssl-1.0.2j-x64-windows.tar.lzma 7z x -y -oC:\Ruby${Env:ruby_version} openssl-1.0.2j-x64-windows.tar } else { # RubyInstaller2; openssl package seems to be installed already $Env:openssl_dir = "C:\msys64\mingw64" } - ruby -v - rake install_dependencies build_script: - rake -rdevkit compile -- --with-openssl-dir=%openssl_dir% --enable-debug test_script: - rake test OSSL_MDEBUG=1 deploy: off environment: matrix: - ruby_version: "23-x64" # RI - ruby_version: "24-x64" # RI2 openssl-2.0.9/docker-compose.yml000066400000000000000000000003531336157045000166470ustar00rootroot00000000000000compile: &defaults build: . environment: RUBY_VERSION: OPENSSL_VERSION: command: rake compile test: <<: *defaults command: rake compile test OSSL_MDEBUG=1 -- --enable-debug debug: <<: *defaults command: /bin/bash openssl-2.0.9/ext/000077500000000000000000000000001336157045000140115ustar00rootroot00000000000000openssl-2.0.9/ext/openssl/000077500000000000000000000000001336157045000154745ustar00rootroot00000000000000openssl-2.0.9/ext/openssl/deprecation.rb000066400000000000000000000013251336157045000203170ustar00rootroot00000000000000# frozen_string_literal: false module OpenSSL def self.deprecated_warning_flag unless flag = (@deprecated_warning_flag ||= nil) if try_compile("", flag = "-Werror=deprecated-declarations") if /darwin/ =~ RUBY_PLATFORM and with_config("broken-apple-openssl") flag = "-Wno-deprecated-declarations" end $warnflags << " #{flag}" else flag = "" end @deprecated_warning_flag = flag end flag end def self.check_func(func, header) have_func(func, header, deprecated_warning_flag) end def self.check_func_or_macro(func, header) check_func(func, header) or have_macro(func, header) && $defs.push("-DHAVE_#{func.upcase}") end end openssl-2.0.9/ext/openssl/extconf.rb000066400000000000000000000151001336157045000174640ustar00rootroot00000000000000# -*- coding: us-ascii -*- # frozen_string_literal: false =begin = Info 'OpenSSL for Ruby 2' project Copyright (C) 2002 Michal Rokos All rights reserved. = Licence This program is licensed under the same licence as Ruby. (See the file 'LICENCE'.) =end require "mkmf" require File.expand_path('../deprecation', __FILE__) dir_config("openssl") dir_config("kerberos") Logging::message "=== OpenSSL for Ruby configurator ===\n" # Add -Werror=deprecated-declarations to $warnflags if available OpenSSL.deprecated_warning_flag ## # Adds -DOSSL_DEBUG for compilation and some more targets when GCC is used # To turn it on, use: --with-debug or --enable-debug # if with_config("debug") or enable_config("debug") $defs.push("-DOSSL_DEBUG") end Logging::message "=== Checking for system dependent stuff... ===\n" have_library("nsl", "t_open") have_library("socket", "socket") if $mswin || $mingw have_library("ws2_32") end Logging::message "=== Checking for required stuff... ===\n" result = pkg_config("openssl") && have_header("openssl/ssl.h") def find_openssl_library if $mswin || $mingw # required for static OpenSSL libraries have_library("gdi32") # OpenSSL <= 1.0.2 (for RAND_screen()) have_library("crypt32") end return false unless have_header("openssl/ssl.h") ret = have_library("crypto", "CRYPTO_malloc") && have_library("ssl", "SSL_new") return ret if ret if $mswin # OpenSSL >= 1.1.0: libcrypto.lib and libssl.lib. if have_library("libcrypto", "CRYPTO_malloc") && have_library("libssl", "SSL_new") return true end # OpenSSL <= 1.0.2: libeay32.lib and ssleay32.lib. if have_library("libeay32", "CRYPTO_malloc") && have_library("ssleay32", "SSL_new") return true end # LibreSSL: libcrypto-##.lib and libssl-##.lib, where ## is the ABI version # number. We have to find the version number out by scanning libpath. libpath = $LIBPATH.dup libpath |= ENV["LIB"].split(File::PATH_SEPARATOR) libpath.map! { |d| d.tr(File::ALT_SEPARATOR, File::SEPARATOR) } ret = [ ["crypto", "CRYPTO_malloc"], ["ssl", "SSL_new"] ].all? do |base, func| result = false libs = ["lib#{base}-[0-9][0-9]", "lib#{base}-[0-9][0-9][0-9]"] libs = Dir.glob(libs.map{|l| libpath.map{|d| File.join(d, l + ".*")}}.flatten).map{|path| File.basename(path, ".*")}.uniq libs.each do |lib| result = have_library(lib, func) break if result end result end return ret if ret end return false end unless result unless find_openssl_library Logging::message "=== Checking for required stuff failed. ===\n" Logging::message "Makefile wasn't created. Fix the errors above.\n" exit 1 end end result = checking_for("OpenSSL version is 0.9.8 or later") { try_static_assert("OPENSSL_VERSION_NUMBER >= 0x00908000L", "openssl/opensslv.h") } unless result raise "OpenSSL 0.9.8 or later required." end if /darwin/ =~ RUBY_PLATFORM and !OpenSSL.check_func("SSL_library_init()", "openssl/ssl.h") raise "Ignore OpenSSL broken by Apple.\nPlease use another openssl. (e.g. using `configure --with-openssl-dir=/path/to/openssl')" end Logging::message "=== Checking for OpenSSL features... ===\n" # compile options # SSLv2 and SSLv3 may be removed in future versions of OpenSSL, and even macros # like OPENSSL_NO_SSL2 may not be defined. have_func("SSLv2_method") have_func("SSLv3_method") have_func("TLSv1_1_method") have_func("TLSv1_2_method") have_func("RAND_egd") engines = %w{builtin_engines openbsd_dev_crypto dynamic 4758cca aep atalla chil cswift nuron sureware ubsec padlock capi gmp gost cryptodev aesni} engines.each { |name| OpenSSL.check_func_or_macro("ENGINE_load_#{name}", "openssl/engine.h") } if ($mswin || $mingw) && have_macro("LIBRESSL_VERSION_NUMBER", "openssl/opensslv.h") $defs.push("-DNOCRYPT") end # added in 0.9.8X have_func("EVP_CIPHER_CTX_new") have_func("EVP_CIPHER_CTX_free") OpenSSL.check_func_or_macro("SSL_CTX_clear_options", "openssl/ssl.h") # added in 1.0.0 have_func("ASN1_TIME_adj") have_func("EVP_CIPHER_CTX_copy") have_func("EVP_PKEY_base_id") have_func("HMAC_CTX_copy") have_func("PKCS5_PBKDF2_HMAC") have_func("X509_NAME_hash_old") have_func("X509_STORE_CTX_get0_current_crl") have_func("X509_STORE_set_verify_cb") have_func("i2d_ASN1_SET_ANY") have_func("SSL_SESSION_cmp") # removed OpenSSL.check_func_or_macro("SSL_set_tlsext_host_name", "openssl/ssl.h") have_struct_member("CRYPTO_THREADID", "ptr", "openssl/crypto.h") have_func("EVP_PKEY_get0") # added in 1.0.1 have_func("SSL_CTX_set_next_proto_select_cb") have_macro("EVP_CTRL_GCM_GET_TAG", ['openssl/evp.h']) && $defs.push("-DHAVE_AUTHENTICATED_ENCRYPTION") # added in 1.0.2 have_func("EC_curve_nist2nid") have_func("X509_REVOKED_dup") have_func("X509_STORE_CTX_get0_store") have_func("SSL_CTX_set_alpn_select_cb") OpenSSL.check_func_or_macro("SSL_CTX_set1_curves_list", "openssl/ssl.h") OpenSSL.check_func_or_macro("SSL_CTX_set_ecdh_auto", "openssl/ssl.h") OpenSSL.check_func_or_macro("SSL_get_server_tmp_key", "openssl/ssl.h") have_func("SSL_is_server") # added in 1.1.0 if !have_struct_member("SSL", "ctx", "openssl/ssl.h") || try_static_assert("LIBRESSL_VERSION_NUMBER >= 0x2070000fL", "openssl/opensslv.h") $defs.push("-DHAVE_OPAQUE_OPENSSL") end have_func("CRYPTO_lock") || $defs.push("-DHAVE_OPENSSL_110_THREADING_API") have_func("BN_GENCB_new") have_func("BN_GENCB_free") have_func("BN_GENCB_get_arg") have_func("EVP_MD_CTX_new") have_func("EVP_MD_CTX_free") have_func("HMAC_CTX_new") have_func("HMAC_CTX_free") OpenSSL.check_func("RAND_pseudo_bytes", "openssl/rand.h") # deprecated have_func("X509_STORE_get_ex_data") have_func("X509_STORE_set_ex_data") have_func("X509_CRL_get0_signature") have_func("X509_REQ_get0_signature") have_func("X509_REVOKED_get0_serialNumber") have_func("X509_REVOKED_get0_revocationDate") have_func("X509_get0_tbs_sigalg") have_func("X509_STORE_CTX_get0_untrusted") have_func("X509_STORE_CTX_get0_cert") have_func("X509_STORE_CTX_get0_chain") have_func("OCSP_SINGLERESP_get0_id") have_func("SSL_CTX_get_ciphers") have_func("X509_up_ref") have_func("X509_CRL_up_ref") have_func("X509_STORE_up_ref") have_func("SSL_SESSION_up_ref") have_func("EVP_PKEY_up_ref") OpenSSL.check_func_or_macro("SSL_CTX_set_tmp_ecdh_callback", "openssl/ssl.h") # removed OpenSSL.check_func_or_macro("SSL_CTX_set_min_proto_version", "openssl/ssl.h") have_func("SSL_CTX_get_security_level") have_func("X509_get0_notBefore") have_func("SSL_SESSION_get_protocol_version") Logging::message "=== Checking done. ===\n" create_header create_makefile("openssl") Logging::message "Done.\n" openssl-2.0.9/ext/openssl/openssl_missing.c000066400000000000000000000071261336157045000210620ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include RUBY_EXTCONF_H #include /* memcpy() */ #if !defined(OPENSSL_NO_ENGINE) # include #endif #if !defined(OPENSSL_NO_HMAC) # include #endif #include #include "openssl_missing.h" /* added in 0.9.8X */ #if !defined(HAVE_EVP_CIPHER_CTX_NEW) EVP_CIPHER_CTX * ossl_EVP_CIPHER_CTX_new(void) { EVP_CIPHER_CTX *ctx = OPENSSL_malloc(sizeof(EVP_CIPHER_CTX)); if (!ctx) return NULL; EVP_CIPHER_CTX_init(ctx); return ctx; } #endif #if !defined(HAVE_EVP_CIPHER_CTX_FREE) void ossl_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx) { if (ctx) { EVP_CIPHER_CTX_cleanup(ctx); OPENSSL_free(ctx); } } #endif /* added in 1.0.0 */ #if !defined(HAVE_EVP_CIPHER_CTX_COPY) /* * this function does not exist in OpenSSL yet... or ever?. * a future version may break this function. * tested on 0.9.7d. */ int ossl_EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *out, const EVP_CIPHER_CTX *in) { memcpy(out, in, sizeof(EVP_CIPHER_CTX)); #if !defined(OPENSSL_NO_ENGINE) if (in->engine) ENGINE_add(out->engine); if (in->cipher_data) { out->cipher_data = OPENSSL_malloc(in->cipher->ctx_size); memcpy(out->cipher_data, in->cipher_data, in->cipher->ctx_size); } #endif return 1; } #endif #if !defined(OPENSSL_NO_HMAC) #if !defined(HAVE_HMAC_CTX_COPY) int ossl_HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in) { if (!out || !in) return 0; memcpy(out, in, sizeof(HMAC_CTX)); EVP_MD_CTX_copy(&out->md_ctx, &in->md_ctx); EVP_MD_CTX_copy(&out->i_ctx, &in->i_ctx); EVP_MD_CTX_copy(&out->o_ctx, &in->o_ctx); return 1; } #endif /* HAVE_HMAC_CTX_COPY */ #endif /* NO_HMAC */ /* added in 1.0.2 */ #if !defined(OPENSSL_NO_EC) #if !defined(HAVE_EC_CURVE_NIST2NID) static struct { const char *name; int nid; } nist_curves[] = { {"B-163", NID_sect163r2}, {"B-233", NID_sect233r1}, {"B-283", NID_sect283r1}, {"B-409", NID_sect409r1}, {"B-571", NID_sect571r1}, {"K-163", NID_sect163k1}, {"K-233", NID_sect233k1}, {"K-283", NID_sect283k1}, {"K-409", NID_sect409k1}, {"K-571", NID_sect571k1}, {"P-192", NID_X9_62_prime192v1}, {"P-224", NID_secp224r1}, {"P-256", NID_X9_62_prime256v1}, {"P-384", NID_secp384r1}, {"P-521", NID_secp521r1} }; int ossl_EC_curve_nist2nid(const char *name) { size_t i; for (i = 0; i < (sizeof(nist_curves) / sizeof(nist_curves[0])); i++) { if (!strcmp(nist_curves[i].name, name)) return nist_curves[i].nid; } return NID_undef; } #endif #endif /*** added in 1.1.0 ***/ #if !defined(HAVE_HMAC_CTX_NEW) HMAC_CTX * ossl_HMAC_CTX_new(void) { HMAC_CTX *ctx = OPENSSL_malloc(sizeof(HMAC_CTX)); if (!ctx) return NULL; HMAC_CTX_init(ctx); return ctx; } #endif #if !defined(HAVE_HMAC_CTX_FREE) void ossl_HMAC_CTX_free(HMAC_CTX *ctx) { if (ctx) { HMAC_CTX_cleanup(ctx); OPENSSL_free(ctx); } } #endif #if !defined(HAVE_X509_CRL_GET0_SIGNATURE) void ossl_X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig, const X509_ALGOR **palg) { if (psig != NULL) *psig = crl->signature; if (palg != NULL) *palg = crl->sig_alg; } #endif #if !defined(HAVE_X509_REQ_GET0_SIGNATURE) void ossl_X509_REQ_get0_signature(const X509_REQ *req, const ASN1_BIT_STRING **psig, const X509_ALGOR **palg) { if (psig != NULL) *psig = req->signature; if (palg != NULL) *palg = req->sig_alg; } #endif openssl-2.0.9/ext/openssl/openssl_missing.h000066400000000000000000000202161336157045000210620ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_OPENSSL_MISSING_H_) #define _OSSL_OPENSSL_MISSING_H_ #include "ruby/config.h" /* added in 0.9.8X */ #if !defined(HAVE_EVP_CIPHER_CTX_NEW) EVP_CIPHER_CTX *ossl_EVP_CIPHER_CTX_new(void); # define EVP_CIPHER_CTX_new ossl_EVP_CIPHER_CTX_new #endif #if !defined(HAVE_EVP_CIPHER_CTX_FREE) void ossl_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *); # define EVP_CIPHER_CTX_free ossl_EVP_CIPHER_CTX_free #endif #if !defined(HAVE_SSL_CTX_CLEAR_OPTIONS) # define SSL_CTX_clear_options(ctx, op) ((ctx)->options &= ~(op)) #endif /* added in 1.0.0 */ #if !defined(HAVE_EVP_PKEY_BASE_ID) # define EVP_PKEY_base_id(pkey) EVP_PKEY_type((pkey)->type) #endif #if !defined(HAVE_EVP_CIPHER_CTX_COPY) int ossl_EVP_CIPHER_CTX_copy(EVP_CIPHER_CTX *, const EVP_CIPHER_CTX *); # define EVP_CIPHER_CTX_copy ossl_EVP_CIPHER_CTX_copy #endif #if !defined(HAVE_HMAC_CTX_COPY) int ossl_HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in); # define HMAC_CTX_copy ossl_HMAC_CTX_copy #endif #if !defined(HAVE_X509_STORE_CTX_GET0_CURRENT_CRL) # define X509_STORE_CTX_get0_current_crl(x) ((x)->current_crl) #endif #if !defined(HAVE_X509_STORE_SET_VERIFY_CB) # define X509_STORE_set_verify_cb X509_STORE_set_verify_cb_func #endif #if !defined(HAVE_I2D_ASN1_SET_ANY) # define i2d_ASN1_SET_ANY(sk, x) i2d_ASN1_SET_OF_ASN1_TYPE((sk), (x), \ i2d_ASN1_TYPE, V_ASN1_SET, V_ASN1_UNIVERSAL, 0) #endif #if !defined(HAVE_EVP_PKEY_GET0) # define EVP_PKEY_get0(pk) (pk->pkey.ptr) #endif /* added in 1.0.2 */ #if !defined(OPENSSL_NO_EC) #if !defined(HAVE_EC_CURVE_NIST2NID) int ossl_EC_curve_nist2nid(const char *); # define EC_curve_nist2nid ossl_EC_curve_nist2nid #endif #endif #if !defined(HAVE_X509_REVOKED_DUP) # define X509_REVOKED_dup(rev) (X509_REVOKED *)ASN1_dup((i2d_of_void *)i2d_X509_REVOKED, \ (d2i_of_void *)d2i_X509_REVOKED, (char *)(rev)) #endif #if !defined(HAVE_X509_STORE_CTX_GET0_STORE) # define X509_STORE_CTX_get0_store(x) ((x)->ctx) #endif #if !defined(HAVE_SSL_IS_SERVER) # define SSL_is_server(s) ((s)->server) #endif /* added in 1.1.0 */ #if !defined(HAVE_BN_GENCB_NEW) # define BN_GENCB_new() ((BN_GENCB *)OPENSSL_malloc(sizeof(BN_GENCB))) #endif #if !defined(HAVE_BN_GENCB_FREE) # define BN_GENCB_free(cb) OPENSSL_free(cb) #endif #if !defined(HAVE_BN_GENCB_GET_ARG) # define BN_GENCB_get_arg(cb) (cb)->arg #endif #if !defined(HAVE_EVP_MD_CTX_NEW) # define EVP_MD_CTX_new EVP_MD_CTX_create #endif #if !defined(HAVE_EVP_MD_CTX_FREE) # define EVP_MD_CTX_free EVP_MD_CTX_destroy #endif #if !defined(HAVE_HMAC_CTX_NEW) HMAC_CTX *ossl_HMAC_CTX_new(void); # define HMAC_CTX_new ossl_HMAC_CTX_new #endif #if !defined(HAVE_HMAC_CTX_FREE) void ossl_HMAC_CTX_free(HMAC_CTX *); # define HMAC_CTX_free ossl_HMAC_CTX_free #endif #if !defined(HAVE_X509_STORE_GET_EX_DATA) # define X509_STORE_get_ex_data(x, idx) \ CRYPTO_get_ex_data(&(x)->ex_data, (idx)) #endif #if !defined(HAVE_X509_STORE_SET_EX_DATA) # define X509_STORE_set_ex_data(x, idx, data) \ CRYPTO_set_ex_data(&(x)->ex_data, (idx), (data)) # define X509_STORE_get_ex_new_index(l, p, newf, dupf, freef) \ CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_X509_STORE, (l), (p), \ (newf), (dupf), (freef)) #endif #if !defined(HAVE_X509_CRL_GET0_SIGNATURE) void ossl_X509_CRL_get0_signature(const X509_CRL *, const ASN1_BIT_STRING **, const X509_ALGOR **); # define X509_CRL_get0_signature ossl_X509_CRL_get0_signature #endif #if !defined(HAVE_X509_REQ_GET0_SIGNATURE) void ossl_X509_REQ_get0_signature(const X509_REQ *, const ASN1_BIT_STRING **, const X509_ALGOR **); # define X509_REQ_get0_signature ossl_X509_REQ_get0_signature #endif #if !defined(HAVE_X509_REVOKED_GET0_SERIALNUMBER) # define X509_REVOKED_get0_serialNumber(x) ((x)->serialNumber) #endif #if !defined(HAVE_X509_REVOKED_GET0_REVOCATIONDATE) # define X509_REVOKED_get0_revocationDate(x) ((x)->revocationDate) #endif #if !defined(HAVE_X509_GET0_TBS_SIGALG) # define X509_get0_tbs_sigalg(x) ((x)->cert_info->signature) #endif #if !defined(HAVE_X509_STORE_CTX_GET0_UNTRUSTED) # define X509_STORE_CTX_get0_untrusted(x) ((x)->untrusted) #endif #if !defined(HAVE_X509_STORE_CTX_GET0_CERT) # define X509_STORE_CTX_get0_cert(x) ((x)->cert) #endif #if !defined(HAVE_X509_STORE_CTX_GET0_CHAIN) # define X509_STORE_CTX_get0_chain(ctx) X509_STORE_CTX_get_chain(ctx) #endif #if !defined(HAVE_OCSP_SINGLERESP_GET0_ID) # define OCSP_SINGLERESP_get0_id(s) ((s)->certId) #endif #if !defined(HAVE_SSL_CTX_GET_CIPHERS) # define SSL_CTX_get_ciphers(ctx) ((ctx)->cipher_list) #endif #if !defined(HAVE_X509_UP_REF) # define X509_up_ref(x) \ CRYPTO_add(&(x)->references, 1, CRYPTO_LOCK_X509) #endif #if !defined(HAVE_X509_CRL_UP_REF) # define X509_CRL_up_ref(x) \ CRYPTO_add(&(x)->references, 1, CRYPTO_LOCK_X509_CRL); #endif #if !defined(HAVE_X509_STORE_UP_REF) # define X509_STORE_up_ref(x) \ CRYPTO_add(&(x)->references, 1, CRYPTO_LOCK_X509_STORE); #endif #if !defined(HAVE_SSL_SESSION_UP_REF) # define SSL_SESSION_up_ref(x) \ CRYPTO_add(&(x)->references, 1, CRYPTO_LOCK_SSL_SESSION); #endif #if !defined(HAVE_EVP_PKEY_UP_REF) # define EVP_PKEY_up_ref(x) \ CRYPTO_add(&(x)->references, 1, CRYPTO_LOCK_EVP_PKEY); #endif #if !defined(HAVE_OPAQUE_OPENSSL) #define IMPL_PKEY_GETTER(_type, _name) \ static inline _type *EVP_PKEY_get0_##_type(EVP_PKEY *pkey) { \ return pkey->pkey._name; } #define IMPL_KEY_ACCESSOR2(_type, _group, a1, a2, _fail_cond) \ static inline void _type##_get0_##_group(const _type *obj, const BIGNUM **a1, const BIGNUM **a2) { \ if (a1) *a1 = obj->a1; \ if (a2) *a2 = obj->a2; } \ static inline int _type##_set0_##_group(_type *obj, BIGNUM *a1, BIGNUM *a2) { \ if (_fail_cond) return 0; \ BN_clear_free(obj->a1); obj->a1 = a1; \ BN_clear_free(obj->a2); obj->a2 = a2; \ return 1; } #define IMPL_KEY_ACCESSOR3(_type, _group, a1, a2, a3, _fail_cond) \ static inline void _type##_get0_##_group(const _type *obj, const BIGNUM **a1, const BIGNUM **a2, const BIGNUM **a3) { \ if (a1) *a1 = obj->a1; \ if (a2) *a2 = obj->a2; \ if (a3) *a3 = obj->a3; } \ static inline int _type##_set0_##_group(_type *obj, BIGNUM *a1, BIGNUM *a2, BIGNUM *a3) { \ if (_fail_cond) return 0; \ BN_clear_free(obj->a1); obj->a1 = a1; \ BN_clear_free(obj->a2); obj->a2 = a2; \ BN_clear_free(obj->a3); obj->a3 = a3; \ return 1; } #if !defined(OPENSSL_NO_RSA) IMPL_PKEY_GETTER(RSA, rsa) IMPL_KEY_ACCESSOR3(RSA, key, n, e, d, (n == obj->n || e == obj->e || (obj->d && d == obj->d))) IMPL_KEY_ACCESSOR2(RSA, factors, p, q, (p == obj->p || q == obj->q)) IMPL_KEY_ACCESSOR3(RSA, crt_params, dmp1, dmq1, iqmp, (dmp1 == obj->dmp1 || dmq1 == obj->dmq1 || iqmp == obj->iqmp)) #endif #if !defined(OPENSSL_NO_DSA) IMPL_PKEY_GETTER(DSA, dsa) IMPL_KEY_ACCESSOR2(DSA, key, pub_key, priv_key, (pub_key == obj->pub_key || (obj->priv_key && priv_key == obj->priv_key))) IMPL_KEY_ACCESSOR3(DSA, pqg, p, q, g, (p == obj->p || q == obj->q || g == obj->g)) #endif #if !defined(OPENSSL_NO_DH) IMPL_PKEY_GETTER(DH, dh) IMPL_KEY_ACCESSOR2(DH, key, pub_key, priv_key, (pub_key == obj->pub_key || (obj->priv_key && priv_key == obj->priv_key))) IMPL_KEY_ACCESSOR3(DH, pqg, p, q, g, (p == obj->p || obj->q && q == obj->q || g == obj->g)) static inline ENGINE *DH_get0_engine(DH *dh) { return dh->engine; } #endif #if !defined(OPENSSL_NO_EC) IMPL_PKEY_GETTER(EC_KEY, ec) #endif #undef IMPL_PKEY_GETTER #undef IMPL_KEY_ACCESSOR2 #undef IMPL_KEY_ACCESSOR3 #endif /* HAVE_OPAQUE_OPENSSL */ #if defined(HAVE_AUTHENTICATED_ENCRYPTION) && !defined(EVP_CTRL_AEAD_GET_TAG) # define EVP_CTRL_AEAD_GET_TAG EVP_CTRL_GCM_GET_TAG # define EVP_CTRL_AEAD_SET_TAG EVP_CTRL_GCM_SET_TAG # define EVP_CTRL_AEAD_SET_IVLEN EVP_CTRL_GCM_SET_IVLEN #endif #if !defined(HAVE_X509_GET0_NOTBEFORE) # define X509_get0_notBefore(x) X509_get_notBefore(x) # define X509_get0_notAfter(x) X509_get_notAfter(x) # define X509_CRL_get0_lastUpdate(x) X509_CRL_get_lastUpdate(x) # define X509_CRL_get0_nextUpdate(x) X509_CRL_get_nextUpdate(x) #endif #if !defined(HAVE_SSL_SESSION_GET_PROTOCOL_VERSION) # define SSL_SESSION_get_protocol_version(s) ((s)->ssl_version) #endif #endif /* _OSSL_OPENSSL_MISSING_H_ */ openssl-2.0.9/ext/openssl/ossl.c000066400000000000000000001000331336157045000166150ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #include /* for ossl_raise */ #include /* for OpenSSL < 1.1.0 locks */ /* * Data Conversion */ #define OSSL_IMPL_ARY2SK(name, type, expected_class, dup) \ STACK_OF(type) * \ ossl_##name##_ary2sk0(VALUE ary) \ { \ STACK_OF(type) *sk; \ VALUE val; \ type *x; \ int i; \ \ Check_Type(ary, T_ARRAY); \ sk = sk_##type##_new_null(); \ if (!sk) ossl_raise(eOSSLError, NULL); \ \ for (i = 0; i < RARRAY_LEN(ary); i++) { \ val = rb_ary_entry(ary, i); \ if (!rb_obj_is_kind_of(val, expected_class)) { \ sk_##type##_pop_free(sk, type##_free); \ ossl_raise(eOSSLError, "object in array not" \ " of class ##type##"); \ } \ x = dup(val); /* NEED TO DUP */ \ sk_##type##_push(sk, x); \ } \ return sk; \ } \ \ STACK_OF(type) * \ ossl_protect_##name##_ary2sk(VALUE ary, int *status) \ { \ return (STACK_OF(type)*)rb_protect( \ (VALUE (*)(VALUE))ossl_##name##_ary2sk0, \ ary, \ status); \ } \ \ STACK_OF(type) * \ ossl_##name##_ary2sk(VALUE ary) \ { \ STACK_OF(type) *sk; \ int status = 0; \ \ sk = ossl_protect_##name##_ary2sk(ary, &status); \ if (status) rb_jump_tag(status); \ \ return sk; \ } OSSL_IMPL_ARY2SK(x509, X509, cX509Cert, DupX509CertPtr) #define OSSL_IMPL_SK2ARY(name, type) \ VALUE \ ossl_##name##_sk2ary(const STACK_OF(type) *sk) \ { \ type *t; \ int i, num; \ VALUE ary; \ \ if (!sk) { \ OSSL_Debug("empty sk!"); \ return Qnil; \ } \ num = sk_##type##_num(sk); \ if (num < 0) { \ OSSL_Debug("items in sk < -1???"); \ return rb_ary_new(); \ } \ ary = rb_ary_new2(num); \ \ for (i=0; i> 4]; out[i * 2 + 1] = hex[p & 0x0f]; } } /* * our default PEM callback */ VALUE ossl_pem_passwd_value(VALUE pass) { if (NIL_P(pass)) return Qnil; StringValue(pass); /* PEM_BUFSIZE is currently used as the second argument of pem_password_cb, * that is +max_len+ of ossl_pem_passwd_cb() */ if (RSTRING_LEN(pass) > PEM_BUFSIZE) ossl_raise(eOSSLError, "password must not be longer than %d bytes", PEM_BUFSIZE); return pass; } static VALUE ossl_pem_passwd_cb0(VALUE flag) { VALUE pass = rb_yield(flag); if (NIL_P(pass)) return Qnil; StringValue(pass); return pass; } int ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd_) { long len; int status; VALUE rflag, pass = (VALUE)pwd_; if (RTEST(pass)) { /* PEM_def_callback(buf, max_len, flag, StringValueCStr(pass)) does not * work because it does not allow NUL characters and truncates to 1024 * bytes silently if the input is over 1024 bytes */ if (RB_TYPE_P(pass, T_STRING)) { len = RSTRING_LEN(pass); if (len <= max_len) { memcpy(buf, RSTRING_PTR(pass), len); return (int)len; } } OSSL_Debug("passed data is not valid String???"); return -1; } if (!rb_block_given_p()) { return PEM_def_callback(buf, max_len, flag, NULL); } while (1) { /* * when the flag is nonzero, this passphrase * will be used to perform encryption; otherwise it will * be used to perform decryption. */ rflag = flag ? Qtrue : Qfalse; pass = rb_protect(ossl_pem_passwd_cb0, rflag, &status); if (status) { /* ignore an exception raised. */ rb_set_errinfo(Qnil); return -1; } if (NIL_P(pass)) return -1; len = RSTRING_LEN(pass); if (len > max_len) { rb_warning("password must not be longer than %d bytes", max_len); continue; } memcpy(buf, RSTRING_PTR(pass), len); break; } return (int)len; } /* * main module */ VALUE mOSSL; /* * OpenSSLError < StandardError */ VALUE eOSSLError; /* * Convert to DER string */ ID ossl_s_to_der; VALUE ossl_to_der(VALUE obj) { VALUE tmp; tmp = rb_funcall(obj, ossl_s_to_der, 0); StringValue(tmp); return tmp; } VALUE ossl_to_der_if_possible(VALUE obj) { if(rb_respond_to(obj, ossl_s_to_der)) return ossl_to_der(obj); return obj; } /* * Errors */ static VALUE ossl_make_error(VALUE exc, const char *fmt, va_list args) { VALUE str = Qnil; const char *msg; long e; e = ERR_peek_last_error(); if (fmt) { str = rb_vsprintf(fmt, args); } if (e) { if (dOSSL == Qtrue) /* FULL INFO */ msg = ERR_error_string(e, NULL); else msg = ERR_reason_error_string(e); if (NIL_P(str)) { if (msg) str = rb_str_new_cstr(msg); } else { if (RSTRING_LEN(str)) rb_str_cat2(str, ": "); rb_str_cat2(str, msg ? msg : "(null)"); } } ossl_clear_error(); if (NIL_P(str)) str = rb_str_new(0, 0); return rb_exc_new3(exc, str); } void ossl_raise(VALUE exc, const char *fmt, ...) { va_list args; VALUE err; va_start(args, fmt); err = ossl_make_error(exc, fmt, args); va_end(args); rb_exc_raise(err); } void ossl_clear_error(void) { if (dOSSL == Qtrue) { unsigned long e; const char *file, *data, *errstr; int line, flags; while ((e = ERR_get_error_line_data(&file, &line, &data, &flags))) { errstr = ERR_error_string(e, NULL); if (!errstr) errstr = "(null)"; if (flags & ERR_TXT_STRING) { if (!data) data = "(null)"; rb_warn("error on stack: %s (%s)", errstr, data); } else { rb_warn("error on stack: %s", errstr); } } } else { ERR_clear_error(); } } /* * call-seq: * OpenSSL.errors -> [String...] * * See any remaining errors held in queue. * * Any errors you see here are probably due to a bug in ruby's OpenSSL implementation. */ VALUE ossl_get_errors(void) { VALUE ary; long e; ary = rb_ary_new(); while ((e = ERR_get_error()) != 0){ rb_ary_push(ary, rb_str_new2(ERR_error_string(e, NULL))); } return ary; } /* * Debug */ VALUE dOSSL; #if !defined(HAVE_VA_ARGS_MACRO) void ossl_debug(const char *fmt, ...) { va_list args; if (dOSSL == Qtrue) { fprintf(stderr, "OSSL_DEBUG: "); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, " [CONTEXT N/A]\n"); } } #endif /* * call-seq: * OpenSSL.debug -> true | false */ static VALUE ossl_debug_get(VALUE self) { return dOSSL; } /* * call-seq: * OpenSSL.debug = boolean -> boolean * * Turns on or off debug mode. With debug mode, all erros added to the OpenSSL * error queue will be printed to stderr. */ static VALUE ossl_debug_set(VALUE self, VALUE val) { dOSSL = RTEST(val) ? Qtrue : Qfalse; return val; } /* * call-seq: * OpenSSL.fips_mode = boolean -> boolean * * Turns FIPS mode on or off. Turning on FIPS mode will obviously only have an * effect for FIPS-capable installations of the OpenSSL library. Trying to do * so otherwise will result in an error. * * === Examples * OpenSSL.fips_mode = true # turn FIPS mode on * OpenSSL.fips_mode = false # and off again */ static VALUE ossl_fips_mode_set(VALUE self, VALUE enabled) { #ifdef OPENSSL_FIPS if (RTEST(enabled)) { int mode = FIPS_mode(); if(!mode && !FIPS_mode_set(1)) /* turning on twice leads to an error */ ossl_raise(eOSSLError, "Turning on FIPS mode failed"); } else { if(!FIPS_mode_set(0)) /* turning off twice is OK */ ossl_raise(eOSSLError, "Turning off FIPS mode failed"); } return enabled; #else if (RTEST(enabled)) ossl_raise(eOSSLError, "This version of OpenSSL does not support FIPS mode"); return enabled; #endif } #if defined(OSSL_DEBUG) #if !defined(LIBRESSL_VERSION_NUMBER) && \ (OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(OPENSSL_NO_CRYPTO_MDEBUG) || \ defined(CRYPTO_malloc_debug_init)) /* * call-seq: * OpenSSL.mem_check_start -> nil * * Calls CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON). Starts tracking memory * allocations. See also OpenSSL.print_mem_leaks. * * This is available only when built with a capable OpenSSL and --enable-debug * configure option. */ static VALUE mem_check_start(VALUE self) { CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); return Qnil; } /* * call-seq: * OpenSSL.print_mem_leaks -> true | false * * For debugging the Ruby/OpenSSL library. Calls CRYPTO_mem_leaks_fp(stderr). * Prints detected memory leaks to standard error. This cleans the global state * up thus you cannot use any methods of the library after calling this. * * Returns true if leaks detected, false otherwise. * * This is available only when built with a capable OpenSSL and --enable-debug * configure option. * * === Example * OpenSSL.mem_check_start * NOT_GCED = OpenSSL::PKey::RSA.new(256) * * END { * GC.start * OpenSSL.print_mem_leaks # will print the leakage * } */ static VALUE print_mem_leaks(VALUE self) { #if OPENSSL_VERSION_NUMBER >= 0x10100000 int ret; #endif BN_CTX_free(ossl_bn_ctx); ossl_bn_ctx = NULL; #if OPENSSL_VERSION_NUMBER >= 0x10100000 ret = CRYPTO_mem_leaks_fp(stderr); if (ret < 0) ossl_raise(eOSSLError, "CRYPTO_mem_leaks_fp"); return ret ? Qfalse : Qtrue; #else CRYPTO_mem_leaks_fp(stderr); return Qnil; #endif } #endif #endif #if !defined(HAVE_OPENSSL_110_THREADING_API) /** * Stores locks needed for OpenSSL thread safety */ struct CRYPTO_dynlock_value { rb_nativethread_lock_t lock; rb_nativethread_id_t owner; size_t count; }; static void ossl_lock_init(struct CRYPTO_dynlock_value *l) { rb_nativethread_lock_initialize(&l->lock); l->count = 0; } static void ossl_lock_unlock(int mode, struct CRYPTO_dynlock_value *l) { if (mode & CRYPTO_LOCK) { /* TODO: rb_nativethread_id_t is not necessarily compared with ==. */ rb_nativethread_id_t tid = rb_nativethread_self(); if (l->count && l->owner == tid) { l->count++; return; } rb_nativethread_lock_lock(&l->lock); l->owner = tid; l->count = 1; } else { if (!--l->count) rb_nativethread_lock_unlock(&l->lock); } } static struct CRYPTO_dynlock_value * ossl_dyn_create_callback(const char *file, int line) { /* Do not use xmalloc() here, since it may raise NoMemoryError */ struct CRYPTO_dynlock_value *dynlock = OPENSSL_malloc(sizeof(struct CRYPTO_dynlock_value)); if (dynlock) ossl_lock_init(dynlock); return dynlock; } static void ossl_dyn_lock_callback(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line) { ossl_lock_unlock(mode, l); } static void ossl_dyn_destroy_callback(struct CRYPTO_dynlock_value *l, const char *file, int line) { rb_nativethread_lock_destroy(&l->lock); OPENSSL_free(l); } #ifdef HAVE_CRYPTO_THREADID_PTR static void ossl_threadid_func(CRYPTO_THREADID *id) { /* register native thread id */ CRYPTO_THREADID_set_pointer(id, (void *)rb_nativethread_self()); } #else static unsigned long ossl_thread_id(void) { /* before OpenSSL 1.0, this is 'unsigned long' */ return (unsigned long)rb_nativethread_self(); } #endif static struct CRYPTO_dynlock_value *ossl_locks; static void ossl_lock_callback(int mode, int type, const char *file, int line) { ossl_lock_unlock(mode, &ossl_locks[type]); } static void Init_ossl_locks(void) { int i; int num_locks = CRYPTO_num_locks(); ossl_locks = ALLOC_N(struct CRYPTO_dynlock_value, num_locks); for (i = 0; i < num_locks; i++) ossl_lock_init(&ossl_locks[i]); #ifdef HAVE_CRYPTO_THREADID_PTR CRYPTO_THREADID_set_callback(ossl_threadid_func); #else CRYPTO_set_id_callback(ossl_thread_id); #endif CRYPTO_set_locking_callback(ossl_lock_callback); CRYPTO_set_dynlock_create_callback(ossl_dyn_create_callback); CRYPTO_set_dynlock_lock_callback(ossl_dyn_lock_callback); CRYPTO_set_dynlock_destroy_callback(ossl_dyn_destroy_callback); } #endif /* !HAVE_OPENSSL_110_THREADING_API */ /* * OpenSSL provides SSL, TLS and general purpose cryptography. It wraps the * OpenSSL[http://www.openssl.org/] library. * * = Examples * * All examples assume you have loaded OpenSSL with: * * require 'openssl' * * These examples build atop each other. For example the key created in the * next is used in throughout these examples. * * == Keys * * === Creating a Key * * This example creates a 2048 bit RSA keypair and writes it to the current * directory. * * key = OpenSSL::PKey::RSA.new 2048 * * open 'private_key.pem', 'w' do |io| io.write key.to_pem end * open 'public_key.pem', 'w' do |io| io.write key.public_key.to_pem end * * === Exporting a Key * * Keys saved to disk without encryption are not secure as anyone who gets * ahold of the key may use it unless it is encrypted. In order to securely * export a key you may export it with a pass phrase. * * cipher = OpenSSL::Cipher.new 'AES-128-CBC' * pass_phrase = 'my secure pass phrase goes here' * * key_secure = key.export cipher, pass_phrase * * open 'private.secure.pem', 'w' do |io| * io.write key_secure * end * * OpenSSL::Cipher.ciphers returns a list of available ciphers. * * === Loading a Key * * A key can also be loaded from a file. * * key2 = OpenSSL::PKey::RSA.new File.read 'private_key.pem' * key2.public? # => true * key2.private? # => true * * or * * key3 = OpenSSL::PKey::RSA.new File.read 'public_key.pem' * key3.public? # => true * key3.private? # => false * * === Loading an Encrypted Key * * OpenSSL will prompt you for your pass phrase when loading an encrypted key. * If you will not be able to type in the pass phrase you may provide it when * loading the key: * * key4_pem = File.read 'private.secure.pem' * pass_phrase = 'my secure pass phrase goes here' * key4 = OpenSSL::PKey::RSA.new key4_pem, pass_phrase * * == RSA Encryption * * RSA provides encryption and decryption using the public and private keys. * You can use a variety of padding methods depending upon the intended use of * encrypted data. * * === Encryption & Decryption * * Asymmetric public/private key encryption is slow and victim to attack in * cases where it is used without padding or directly to encrypt larger chunks * of data. Typical use cases for RSA encryption involve "wrapping" a symmetric * key with the public key of the recipient who would "unwrap" that symmetric * key again using their private key. * The following illustrates a simplified example of such a key transport * scheme. It shouldn't be used in practice, though, standardized protocols * should always be preferred. * * wrapped_key = key.public_encrypt key * * A symmetric key encrypted with the public key can only be decrypted with * the corresponding private key of the recipient. * * original_key = key.private_decrypt wrapped_key * * By default PKCS#1 padding will be used, but it is also possible to use * other forms of padding, see PKey::RSA for further details. * * === Signatures * * Using "private_encrypt" to encrypt some data with the private key is * equivalent to applying a digital signature to the data. A verifying * party may validate the signature by comparing the result of decrypting * the signature with "public_decrypt" to the original data. However, * OpenSSL::PKey already has methods "sign" and "verify" that handle * digital signatures in a standardized way - "private_encrypt" and * "public_decrypt" shouldn't be used in practice. * * To sign a document, a cryptographically secure hash of the document is * computed first, which is then signed using the private key. * * digest = OpenSSL::Digest::SHA256.new * signature = key.sign digest, document * * To validate the signature, again a hash of the document is computed and * the signature is decrypted using the public key. The result is then * compared to the hash just computed, if they are equal the signature was * valid. * * digest = OpenSSL::Digest::SHA256.new * if key.verify digest, signature, document * puts 'Valid' * else * puts 'Invalid' * end * * == PBKDF2 Password-based Encryption * * If supported by the underlying OpenSSL version used, Password-based * Encryption should use the features of PKCS5. If not supported or if * required by legacy applications, the older, less secure methods specified * in RFC 2898 are also supported (see below). * * PKCS5 supports PBKDF2 as it was specified in PKCS#5 * v2.0[http://www.rsa.com/rsalabs/node.asp?id=2127]. It still uses a * password, a salt, and additionally a number of iterations that will * slow the key derivation process down. The slower this is, the more work * it requires being able to brute-force the resulting key. * * === Encryption * * The strategy is to first instantiate a Cipher for encryption, and * then to generate a random IV plus a key derived from the password * using PBKDF2. PKCS #5 v2.0 recommends at least 8 bytes for the salt, * the number of iterations largely depends on the hardware being used. * * cipher = OpenSSL::Cipher.new 'AES-128-CBC' * cipher.encrypt * iv = cipher.random_iv * * pwd = 'some hopefully not to easily guessable password' * salt = OpenSSL::Random.random_bytes 16 * iter = 20000 * key_len = cipher.key_len * digest = OpenSSL::Digest::SHA256.new * * key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest) * cipher.key = key * * Now encrypt the data: * * encrypted = cipher.update document * encrypted << cipher.final * * === Decryption * * Use the same steps as before to derive the symmetric AES key, this time * setting the Cipher up for decryption. * * cipher = OpenSSL::Cipher.new 'AES-128-CBC' * cipher.decrypt * cipher.iv = iv # the one generated with #random_iv * * pwd = 'some hopefully not to easily guessable password' * salt = ... # the one generated above * iter = 20000 * key_len = cipher.key_len * digest = OpenSSL::Digest::SHA256.new * * key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest) * cipher.key = key * * Now decrypt the data: * * decrypted = cipher.update encrypted * decrypted << cipher.final * * == PKCS #5 Password-based Encryption * * PKCS #5 is a password-based encryption standard documented at * RFC2898[http://www.ietf.org/rfc/rfc2898.txt]. It allows a short password or * passphrase to be used to create a secure encryption key. If possible, PBKDF2 * as described above should be used if the circumstances allow it. * * PKCS #5 uses a Cipher, a pass phrase and a salt to generate an encryption * key. * * pass_phrase = 'my secure pass phrase goes here' * salt = '8 octets' * * === Encryption * * First set up the cipher for encryption * * encryptor = OpenSSL::Cipher.new 'AES-128-CBC' * encryptor.encrypt * encryptor.pkcs5_keyivgen pass_phrase, salt * * Then pass the data you want to encrypt through * * encrypted = encryptor.update 'top secret document' * encrypted << encryptor.final * * === Decryption * * Use a new Cipher instance set up for decryption * * decryptor = OpenSSL::Cipher.new 'AES-128-CBC' * decryptor.decrypt * decryptor.pkcs5_keyivgen pass_phrase, salt * * Then pass the data you want to decrypt through * * plain = decryptor.update encrypted * plain << decryptor.final * * == X509 Certificates * * === Creating a Certificate * * This example creates a self-signed certificate using an RSA key and a SHA1 * signature. * * key = OpenSSL::PKey::RSA.new 2048 * name = OpenSSL::X509::Name.parse 'CN=nobody/DC=example' * * cert = OpenSSL::X509::Certificate.new * cert.version = 2 * cert.serial = 0 * cert.not_before = Time.now * cert.not_after = Time.now + 3600 * * cert.public_key = key.public_key * cert.subject = name * * === Certificate Extensions * * You can add extensions to the certificate with * OpenSSL::SSL::ExtensionFactory to indicate the purpose of the certificate. * * extension_factory = OpenSSL::X509::ExtensionFactory.new nil, cert * * cert.add_extension \ * extension_factory.create_extension('basicConstraints', 'CA:FALSE', true) * * cert.add_extension \ * extension_factory.create_extension( * 'keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature') * * cert.add_extension \ * extension_factory.create_extension('subjectKeyIdentifier', 'hash') * * The list of supported extensions (and in some cases their possible values) * can be derived from the "objects.h" file in the OpenSSL source code. * * === Signing a Certificate * * To sign a certificate set the issuer and use OpenSSL::X509::Certificate#sign * with a digest algorithm. This creates a self-signed cert because we're using * the same name and key to sign the certificate as was used to create the * certificate. * * cert.issuer = name * cert.sign key, OpenSSL::Digest::SHA1.new * * open 'certificate.pem', 'w' do |io| io.write cert.to_pem end * * === Loading a Certificate * * Like a key, a cert can also be loaded from a file. * * cert2 = OpenSSL::X509::Certificate.new File.read 'certificate.pem' * * === Verifying a Certificate * * Certificate#verify will return true when a certificate was signed with the * given public key. * * raise 'certificate can not be verified' unless cert2.verify key * * == Certificate Authority * * A certificate authority (CA) is a trusted third party that allows you to * verify the ownership of unknown certificates. The CA issues key signatures * that indicate it trusts the user of that key. A user encountering the key * can verify the signature by using the CA's public key. * * === CA Key * * CA keys are valuable, so we encrypt and save it to disk and make sure it is * not readable by other users. * * ca_key = OpenSSL::PKey::RSA.new 2048 * pass_phrase = 'my secure pass phrase goes here' * * cipher = OpenSSL::Cipher.new 'AES-128-CBC' * * open 'ca_key.pem', 'w', 0400 do |io| * io.write ca_key.export(cipher, pass_phrase) * end * * === CA Certificate * * A CA certificate is created the same way we created a certificate above, but * with different extensions. * * ca_name = OpenSSL::X509::Name.parse 'CN=ca/DC=example' * * ca_cert = OpenSSL::X509::Certificate.new * ca_cert.serial = 0 * ca_cert.version = 2 * ca_cert.not_before = Time.now * ca_cert.not_after = Time.now + 86400 * * ca_cert.public_key = ca_key.public_key * ca_cert.subject = ca_name * ca_cert.issuer = ca_name * * extension_factory = OpenSSL::X509::ExtensionFactory.new * extension_factory.subject_certificate = ca_cert * extension_factory.issuer_certificate = ca_cert * * ca_cert.add_extension \ * extension_factory.create_extension('subjectKeyIdentifier', 'hash') * * This extension indicates the CA's key may be used as a CA. * * ca_cert.add_extension \ * extension_factory.create_extension('basicConstraints', 'CA:TRUE', true) * * This extension indicates the CA's key may be used to verify signatures on * both certificates and certificate revocations. * * ca_cert.add_extension \ * extension_factory.create_extension( * 'keyUsage', 'cRLSign,keyCertSign', true) * * Root CA certificates are self-signed. * * ca_cert.sign ca_key, OpenSSL::Digest::SHA1.new * * The CA certificate is saved to disk so it may be distributed to all the * users of the keys this CA will sign. * * open 'ca_cert.pem', 'w' do |io| * io.write ca_cert.to_pem * end * * === Certificate Signing Request * * The CA signs keys through a Certificate Signing Request (CSR). The CSR * contains the information necessary to identify the key. * * csr = OpenSSL::X509::Request.new * csr.version = 0 * csr.subject = name * csr.public_key = key.public_key * csr.sign key, OpenSSL::Digest::SHA1.new * * A CSR is saved to disk and sent to the CA for signing. * * open 'csr.pem', 'w' do |io| * io.write csr.to_pem * end * * === Creating a Certificate from a CSR * * Upon receiving a CSR the CA will verify it before signing it. A minimal * verification would be to check the CSR's signature. * * csr = OpenSSL::X509::Request.new File.read 'csr.pem' * * raise 'CSR can not be verified' unless csr.verify csr.public_key * * After verification a certificate is created, marked for various usages, * signed with the CA key and returned to the requester. * * csr_cert = OpenSSL::X509::Certificate.new * csr_cert.serial = 0 * csr_cert.version = 2 * csr_cert.not_before = Time.now * csr_cert.not_after = Time.now + 600 * * csr_cert.subject = csr.subject * csr_cert.public_key = csr.public_key * csr_cert.issuer = ca_cert.subject * * extension_factory = OpenSSL::X509::ExtensionFactory.new * extension_factory.subject_certificate = csr_cert * extension_factory.issuer_certificate = ca_cert * * csr_cert.add_extension \ * extension_factory.create_extension('basicConstraints', 'CA:FALSE') * * csr_cert.add_extension \ * extension_factory.create_extension( * 'keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature') * * csr_cert.add_extension \ * extension_factory.create_extension('subjectKeyIdentifier', 'hash') * * csr_cert.sign ca_key, OpenSSL::Digest::SHA1.new * * open 'csr_cert.pem', 'w' do |io| * io.write csr_cert.to_pem * end * * == SSL and TLS Connections * * Using our created key and certificate we can create an SSL or TLS connection. * An SSLContext is used to set up an SSL session. * * context = OpenSSL::SSL::SSLContext.new * * === SSL Server * * An SSL server requires the certificate and private key to communicate * securely with its clients: * * context.cert = cert * context.key = key * * Then create an SSLServer with a TCP server socket and the context. Use the * SSLServer like an ordinary TCP server. * * require 'socket' * * tcp_server = TCPServer.new 5000 * ssl_server = OpenSSL::SSL::SSLServer.new tcp_server, context * * loop do * ssl_connection = ssl_server.accept * * data = connection.gets * * response = "I got #{data.dump}" * puts response * * connection.puts "I got #{data.dump}" * connection.close * end * * === SSL client * * An SSL client is created with a TCP socket and the context. * SSLSocket#connect must be called to initiate the SSL handshake and start * encryption. A key and certificate are not required for the client socket. * * Note that SSLSocket#close doesn't close the underlying socket by default. Set * SSLSocket#sync_close to true if you want. * * require 'socket' * * tcp_socket = TCPSocket.new 'localhost', 5000 * ssl_client = OpenSSL::SSL::SSLSocket.new tcp_socket, context * ssl_client.sync_close = true * ssl_client.connect * * ssl_client.puts "hello server!" * puts ssl_client.gets * * ssl_client.close # shutdown the TLS connection and close tcp_socket * * === Peer Verification * * An unverified SSL connection does not provide much security. For enhanced * security the client or server can verify the certificate of its peer. * * The client can be modified to verify the server's certificate against the * certificate authority's certificate: * * context.ca_file = 'ca_cert.pem' * context.verify_mode = OpenSSL::SSL::VERIFY_PEER * * require 'socket' * * tcp_socket = TCPSocket.new 'localhost', 5000 * ssl_client = OpenSSL::SSL::SSLSocket.new tcp_socket, context * ssl_client.connect * * ssl_client.puts "hello server!" * puts ssl_client.gets * * If the server certificate is invalid or context.ca_file is not set * when verifying peers an OpenSSL::SSL::SSLError will be raised. * */ void Init_openssl(void) { #undef rb_intern /* * Init timezone info */ #if 0 tzset(); #endif /* * Init all digests, ciphers */ /* CRYPTO_malloc_init(); */ /* ENGINE_load_builtin_engines(); */ OpenSSL_add_ssl_algorithms(); OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); SSL_load_error_strings(); /* * FIXME: * On unload do: */ #if 0 CONF_modules_unload(1); destroy_ui_method(); EVP_cleanup(); ENGINE_cleanup(); CRYPTO_cleanup_all_ex_data(); ERR_remove_state(0); ERR_free_strings(); #endif /* * Init main module */ mOSSL = rb_define_module("OpenSSL"); rb_global_variable(&mOSSL); /* * OpenSSL ruby extension version */ rb_define_const(mOSSL, "VERSION", rb_str_new2(OSSL_VERSION)); /* * Version of OpenSSL the ruby OpenSSL extension was built with */ rb_define_const(mOSSL, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT)); /* * Version of OpenSSL the ruby OpenSSL extension is running with */ rb_define_const(mOSSL, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION))); /* * Version number of OpenSSL the ruby OpenSSL extension was built with * (base 16) */ rb_define_const(mOSSL, "OPENSSL_VERSION_NUMBER", INT2NUM(OPENSSL_VERSION_NUMBER)); /* * Boolean indicating whether OpenSSL is FIPS-enabled or not */ rb_define_const(mOSSL, "OPENSSL_FIPS", #ifdef OPENSSL_FIPS Qtrue #else Qfalse #endif ); rb_define_module_function(mOSSL, "fips_mode=", ossl_fips_mode_set, 1); /* * Generic error, * common for all classes under OpenSSL module */ eOSSLError = rb_define_class_under(mOSSL,"OpenSSLError",rb_eStandardError); rb_global_variable(&eOSSLError); /* * Init debug core */ dOSSL = Qfalse; rb_global_variable(&dOSSL); rb_define_module_function(mOSSL, "debug", ossl_debug_get, 0); rb_define_module_function(mOSSL, "debug=", ossl_debug_set, 1); rb_define_module_function(mOSSL, "errors", ossl_get_errors, 0); /* * Get ID of to_der */ ossl_s_to_der = rb_intern("to_der"); #if !defined(HAVE_OPENSSL_110_THREADING_API) Init_ossl_locks(); #endif /* * Init components */ Init_ossl_bn(); Init_ossl_cipher(); Init_ossl_config(); Init_ossl_digest(); Init_ossl_hmac(); Init_ossl_ns_spki(); Init_ossl_pkcs12(); Init_ossl_pkcs7(); Init_ossl_pkcs5(); Init_ossl_pkey(); Init_ossl_rand(); Init_ossl_ssl(); Init_ossl_x509(); Init_ossl_ocsp(); Init_ossl_engine(); Init_ossl_asn1(); #if defined(OSSL_DEBUG) /* * For debugging Ruby/OpenSSL. Enable only when built with --enable-debug */ #if !defined(LIBRESSL_VERSION_NUMBER) && \ (OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(OPENSSL_NO_CRYPTO_MDEBUG) || \ defined(CRYPTO_malloc_debug_init)) rb_define_module_function(mOSSL, "mem_check_start", mem_check_start, 0); rb_define_module_function(mOSSL, "print_mem_leaks", print_mem_leaks, 0); #if defined(CRYPTO_malloc_debug_init) /* <= 1.0.2 */ CRYPTO_malloc_debug_init(); #endif #if defined(V_CRYPTO_MDEBUG_ALL) /* <= 1.0.2 */ CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL); #endif #if OPENSSL_VERSION_NUMBER < 0x10100000 /* <= 1.0.2 */ { int i; /* * See crypto/ex_data.c; call def_get_class() immediately to avoid * allocations. 15 is the maximum number that is used as the class index * in OpenSSL 1.0.2. */ for (i = 0; i <= 15; i++) { if (CRYPTO_get_ex_new_index(i, 0, (void *)"ossl-mdebug-dummy", 0, 0, 0) < 0) rb_raise(rb_eRuntimeError, "CRYPTO_get_ex_new_index for " "class index %d failed", i); } } #endif #endif #endif } openssl-2.0.9/ext/openssl/ossl.h000066400000000000000000000112061336157045000166250ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_H_) #define _OSSL_H_ #include RUBY_EXTCONF_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(OPENSSL_NO_ENGINE) # include #endif #if !defined(OPENSSL_NO_OCSP) # include #endif /* * Common Module */ extern VALUE mOSSL; /* * Common Error Class */ extern VALUE eOSSLError; /* * CheckTypes */ #define OSSL_Check_Kind(obj, klass) do {\ if (!rb_obj_is_kind_of((obj), (klass))) {\ ossl_raise(rb_eTypeError, "wrong argument (%"PRIsVALUE")! (Expected kind of %"PRIsVALUE")",\ rb_obj_class(obj), (klass));\ }\ } while (0) #define OSSL_Check_Instance(obj, klass) do {\ if (!rb_obj_is_instance_of((obj), (klass))) {\ ossl_raise(rb_eTypeError, "wrong argument (%"PRIsVALUE")! (Expected instance of %"PRIsVALUE")",\ rb_obj_class(obj), (klass));\ }\ } while (0) #define OSSL_Check_Same_Class(obj1, obj2) do {\ if (!rb_obj_is_instance_of((obj1), rb_obj_class(obj2))) {\ ossl_raise(rb_eTypeError, "wrong argument type");\ }\ } while (0) /* * Data Conversion */ STACK_OF(X509) *ossl_x509_ary2sk0(VALUE); STACK_OF(X509) *ossl_x509_ary2sk(VALUE); STACK_OF(X509) *ossl_protect_x509_ary2sk(VALUE,int*); VALUE ossl_x509_sk2ary(const STACK_OF(X509) *certs); VALUE ossl_x509crl_sk2ary(const STACK_OF(X509_CRL) *crl); VALUE ossl_x509name_sk2ary(const STACK_OF(X509_NAME) *names); VALUE ossl_buf2str(char *buf, int len); #define ossl_str_adjust(str, p) \ do{\ long len = RSTRING_LEN(str);\ long newlen = (long)((p) - (unsigned char*)RSTRING_PTR(str));\ assert(newlen <= len);\ rb_str_set_len((str), newlen);\ }while(0) /* * Convert binary string to hex string. The caller is responsible for * ensuring out has (2 * len) bytes of capacity. */ void ossl_bin2hex(unsigned char *in, char *out, size_t len); /* * Our default PEM callback */ /* Convert the argument to String and validate the length. Note this may raise. */ VALUE ossl_pem_passwd_value(VALUE); /* Can be casted to pem_password_cb. If a password (String) is passed as the * "arbitrary data" (typically the last parameter of PEM_{read,write}_ * functions), uses the value. If not, but a block is given, yields to it. * If not either, fallbacks to PEM_def_callback() which reads from stdin. */ int ossl_pem_passwd_cb(char *, int, int, void *); /* * Clear BIO* with this in PEM/DER fallback scenarios to avoid decoding * errors piling up in OpenSSL::Errors */ #define OSSL_BIO_reset(bio) do { \ (void)BIO_reset((bio)); \ ossl_clear_error(); \ } while (0) /* * ERRor messages */ #define OSSL_ErrMsg() ERR_reason_error_string(ERR_get_error()) NORETURN(void ossl_raise(VALUE, const char *, ...)); /* Clear OpenSSL error queue. If dOSSL is set, rb_warn() them. */ void ossl_clear_error(void); /* * String to DER String */ extern ID ossl_s_to_der; VALUE ossl_to_der(VALUE); VALUE ossl_to_der_if_possible(VALUE); /* * Debug */ extern VALUE dOSSL; #if defined(HAVE_VA_ARGS_MACRO) #define OSSL_Debug(...) do { \ if (dOSSL == Qtrue) { \ fprintf(stderr, "OSSL_DEBUG: "); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, " [%s:%d]\n", __FILE__, __LINE__); \ } \ } while (0) #define OSSL_Warning(fmt, ...) do { \ OSSL_Debug((fmt), ##__VA_ARGS__); \ rb_warning((fmt), ##__VA_ARGS__); \ } while (0) #define OSSL_Warn(fmt, ...) do { \ OSSL_Debug((fmt), ##__VA_ARGS__); \ rb_warn((fmt), ##__VA_ARGS__); \ } while (0) #else void ossl_debug(const char *, ...); #define OSSL_Debug ossl_debug #define OSSL_Warning rb_warning #define OSSL_Warn rb_warn #endif /* * Include all parts */ #include "openssl_missing.h" #include "ruby_missing.h" #include "ossl_asn1.h" #include "ossl_bio.h" #include "ossl_bn.h" #include "ossl_cipher.h" #include "ossl_config.h" #include "ossl_digest.h" #include "ossl_hmac.h" #include "ossl_ns_spki.h" #include "ossl_ocsp.h" #include "ossl_pkcs12.h" #include "ossl_pkcs7.h" #include "ossl_pkcs5.h" #include "ossl_pkey.h" #include "ossl_rand.h" #include "ossl_ssl.h" #include "ossl_version.h" #include "ossl_x509.h" #include "ossl_engine.h" void Init_openssl(void); #endif /* _OSSL_H_ */ openssl-2.0.9/ext/openssl/ossl_asn1.c000066400000000000000000001732521336157045000175540ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' team members * Copyright (C) 2003 * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" static VALUE join_der(VALUE enumerable); static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth, int yield, long *num_read); static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self); static VALUE ossl_asn1eoc_initialize(VALUE self); /* * DATE conversion */ VALUE asn1time_to_time(const ASN1_TIME *time) { struct tm tm; VALUE argv[6]; int count; if (!time || !time->data) return Qnil; memset(&tm, 0, sizeof(struct tm)); switch (time->type) { case V_ASN1_UTCTIME: count = sscanf((const char *)time->data, "%2d%2d%2d%2d%2d%2dZ", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); if (count == 5) { tm.tm_sec = 0; } else if (count != 6) { ossl_raise(rb_eTypeError, "bad UTCTIME format: \"%s\"", time->data); } if (tm.tm_year < 69) { tm.tm_year += 2000; } else { tm.tm_year += 1900; } break; case V_ASN1_GENERALIZEDTIME: count = sscanf((const char *)time->data, "%4d%2d%2d%2d%2d%2dZ", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); if (count == 5) { tm.tm_sec = 0; } else if (count != 6) { ossl_raise(rb_eTypeError, "bad GENERALIZEDTIME format: \"%s\"", time->data); } break; default: rb_warning("unknown time format"); return Qnil; } argv[0] = INT2NUM(tm.tm_year); argv[1] = INT2NUM(tm.tm_mon); argv[2] = INT2NUM(tm.tm_mday); argv[3] = INT2NUM(tm.tm_hour); argv[4] = INT2NUM(tm.tm_min); argv[5] = INT2NUM(tm.tm_sec); return rb_funcall2(rb_cTime, rb_intern("utc"), 6, argv); } #if defined(HAVE_ASN1_TIME_ADJ) void ossl_time_split(VALUE time, time_t *sec, int *days) { VALUE num = rb_Integer(time); if (FIXNUM_P(num)) { time_t t = FIX2LONG(num); *sec = t % 86400; *days = rb_long2int(t / 86400); } else { *days = NUM2INT(rb_funcall(num, rb_intern("/"), 1, INT2FIX(86400))); *sec = NUM2TIMET(rb_funcall(num, rb_intern("%"), 1, INT2FIX(86400))); } } #else time_t time_to_time_t(VALUE time) { return (time_t)NUM2TIMET(rb_Integer(time)); } #endif /* * STRING conversion */ VALUE asn1str_to_str(const ASN1_STRING *str) { return rb_str_new((const char *)str->data, str->length); } /* * ASN1_INTEGER conversions */ VALUE asn1integer_to_num(const ASN1_INTEGER *ai) { BIGNUM *bn; VALUE num; if (!ai) { ossl_raise(rb_eTypeError, "ASN1_INTEGER is NULL!"); } if (ai->type == V_ASN1_ENUMERATED) /* const_cast: workaround for old OpenSSL */ bn = ASN1_ENUMERATED_to_BN((ASN1_ENUMERATED *)ai, NULL); else bn = ASN1_INTEGER_to_BN(ai, NULL); if (!bn) ossl_raise(eOSSLError, NULL); num = ossl_bn_new(bn); BN_free(bn); return num; } ASN1_INTEGER * num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai) { BIGNUM *bn; if (NIL_P(obj)) ossl_raise(rb_eTypeError, "Can't convert nil into Integer"); bn = GetBNPtr(obj); if (!(ai = BN_to_ASN1_INTEGER(bn, ai))) ossl_raise(eOSSLError, NULL); return ai; } /********/ /* * ASN1 module */ #define ossl_asn1_get_value(o) rb_attr_get((o),sivVALUE) #define ossl_asn1_get_tag(o) rb_attr_get((o),sivTAG) #define ossl_asn1_get_tagging(o) rb_attr_get((o),sivTAGGING) #define ossl_asn1_get_tag_class(o) rb_attr_get((o),sivTAG_CLASS) #define ossl_asn1_get_infinite_length(o) rb_attr_get((o),sivINFINITE_LENGTH) #define ossl_asn1_set_value(o,v) rb_ivar_set((o),sivVALUE,(v)) #define ossl_asn1_set_tag(o,v) rb_ivar_set((o),sivTAG,(v)) #define ossl_asn1_set_tagging(o,v) rb_ivar_set((o),sivTAGGING,(v)) #define ossl_asn1_set_tag_class(o,v) rb_ivar_set((o),sivTAG_CLASS,(v)) #define ossl_asn1_set_infinite_length(o,v) rb_ivar_set((o),sivINFINITE_LENGTH,(v)) VALUE mASN1; VALUE eASN1Error; VALUE cASN1Data; VALUE cASN1Primitive; VALUE cASN1Constructive; VALUE cASN1EndOfContent; VALUE cASN1Boolean; /* BOOLEAN */ VALUE cASN1Integer, cASN1Enumerated; /* INTEGER */ VALUE cASN1BitString; /* BIT STRING */ VALUE cASN1OctetString, cASN1UTF8String; /* STRINGs */ VALUE cASN1NumericString, cASN1PrintableString; VALUE cASN1T61String, cASN1VideotexString; VALUE cASN1IA5String, cASN1GraphicString; VALUE cASN1ISO64String, cASN1GeneralString; VALUE cASN1UniversalString, cASN1BMPString; VALUE cASN1Null; /* NULL */ VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */ VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */ VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */ static VALUE sym_IMPLICIT, sym_EXPLICIT; static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE; static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINFINITE_LENGTH, sivUNUSED_BITS; static ID id_each; /* * Ruby to ASN1 converters */ static ASN1_BOOLEAN obj_to_asn1bool(VALUE obj) { if (NIL_P(obj)) ossl_raise(rb_eTypeError, "Can't convert nil into Boolean"); return RTEST(obj) ? 0xff : 0x0; } static ASN1_INTEGER* obj_to_asn1int(VALUE obj) { return num_to_asn1integer(obj, NULL); } static ASN1_BIT_STRING* obj_to_asn1bstr(VALUE obj, long unused_bits) { ASN1_BIT_STRING *bstr; if(unused_bits < 0) unused_bits = 0; StringValue(obj); if(!(bstr = ASN1_BIT_STRING_new())) ossl_raise(eASN1Error, NULL); ASN1_BIT_STRING_set(bstr, (unsigned char *)RSTRING_PTR(obj), RSTRING_LENINT(obj)); bstr->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07); /* clear */ bstr->flags |= ASN1_STRING_FLAG_BITS_LEFT|(unused_bits&0x07); return bstr; } static ASN1_STRING* obj_to_asn1str(VALUE obj) { ASN1_STRING *str; StringValue(obj); if(!(str = ASN1_STRING_new())) ossl_raise(eASN1Error, NULL); ASN1_STRING_set(str, RSTRING_PTR(obj), RSTRING_LENINT(obj)); return str; } static ASN1_NULL* obj_to_asn1null(VALUE obj) { ASN1_NULL *null; if(!NIL_P(obj)) ossl_raise(eASN1Error, "nil expected"); if(!(null = ASN1_NULL_new())) ossl_raise(eASN1Error, NULL); return null; } static ASN1_OBJECT* obj_to_asn1obj(VALUE obj) { ASN1_OBJECT *a1obj; StringValueCStr(obj); a1obj = OBJ_txt2obj(RSTRING_PTR(obj), 0); if(!a1obj) a1obj = OBJ_txt2obj(RSTRING_PTR(obj), 1); if(!a1obj) ossl_raise(eASN1Error, "invalid OBJECT ID %"PRIsVALUE, obj); return a1obj; } static ASN1_UTCTIME * obj_to_asn1utime(VALUE time) { time_t sec; ASN1_UTCTIME *t; #if defined(HAVE_ASN1_TIME_ADJ) int off_days; ossl_time_split(time, &sec, &off_days); if (!(t = ASN1_UTCTIME_adj(NULL, sec, off_days, 0))) #else sec = time_to_time_t(time); if (!(t = ASN1_UTCTIME_set(NULL, sec))) #endif ossl_raise(eASN1Error, NULL); return t; } static ASN1_GENERALIZEDTIME * obj_to_asn1gtime(VALUE time) { time_t sec; ASN1_GENERALIZEDTIME *t; #if defined(HAVE_ASN1_TIME_ADJ) int off_days; ossl_time_split(time, &sec, &off_days); if (!(t = ASN1_GENERALIZEDTIME_adj(NULL, sec, off_days, 0))) #else sec = time_to_time_t(time); if (!(t = ASN1_GENERALIZEDTIME_set(NULL, sec))) #endif ossl_raise(eASN1Error, NULL); return t; } static ASN1_STRING* obj_to_asn1derstr(VALUE obj) { ASN1_STRING *a1str; VALUE str; str = ossl_to_der(obj); if(!(a1str = ASN1_STRING_new())) ossl_raise(eASN1Error, NULL); ASN1_STRING_set(a1str, RSTRING_PTR(str), RSTRING_LENINT(str)); return a1str; } /* * DER to Ruby converters */ static VALUE decode_bool(unsigned char* der, long length) { const unsigned char *p = der; if (length != 3) ossl_raise(eASN1Error, "invalid length for BOOLEAN"); if (p[0] != 1 || p[1] != 1) ossl_raise(eASN1Error, "invalid BOOLEAN"); return p[2] ? Qtrue : Qfalse; } static VALUE decode_int(unsigned char* der, long length) { ASN1_INTEGER *ai; const unsigned char *p; VALUE ret; int status = 0; p = der; if(!(ai = d2i_ASN1_INTEGER(NULL, &p, length))) ossl_raise(eASN1Error, NULL); ret = rb_protect((VALUE (*)(VALUE))asn1integer_to_num, (VALUE)ai, &status); ASN1_INTEGER_free(ai); if(status) rb_jump_tag(status); return ret; } static VALUE decode_bstr(unsigned char* der, long length, long *unused_bits) { ASN1_BIT_STRING *bstr; const unsigned char *p; long len; VALUE ret; p = der; if(!(bstr = d2i_ASN1_BIT_STRING(NULL, &p, length))) ossl_raise(eASN1Error, NULL); len = bstr->length; *unused_bits = 0; if(bstr->flags & ASN1_STRING_FLAG_BITS_LEFT) *unused_bits = bstr->flags & 0x07; ret = rb_str_new((const char *)bstr->data, len); ASN1_BIT_STRING_free(bstr); return ret; } static VALUE decode_enum(unsigned char* der, long length) { ASN1_ENUMERATED *ai; const unsigned char *p; VALUE ret; int status = 0; p = der; if(!(ai = d2i_ASN1_ENUMERATED(NULL, &p, length))) ossl_raise(eASN1Error, NULL); ret = rb_protect((VALUE (*)(VALUE))asn1integer_to_num, (VALUE)ai, &status); ASN1_ENUMERATED_free(ai); if(status) rb_jump_tag(status); return ret; } static VALUE decode_null(unsigned char* der, long length) { ASN1_NULL *null; const unsigned char *p; p = der; if(!(null = d2i_ASN1_NULL(NULL, &p, length))) ossl_raise(eASN1Error, NULL); ASN1_NULL_free(null); return Qnil; } static VALUE decode_obj(unsigned char* der, long length) { ASN1_OBJECT *obj; const unsigned char *p; VALUE ret; int nid; BIO *bio; p = der; if(!(obj = d2i_ASN1_OBJECT(NULL, &p, length))) ossl_raise(eASN1Error, NULL); if((nid = OBJ_obj2nid(obj)) != NID_undef){ ASN1_OBJECT_free(obj); ret = rb_str_new2(OBJ_nid2sn(nid)); } else{ if(!(bio = BIO_new(BIO_s_mem()))){ ASN1_OBJECT_free(obj); ossl_raise(eASN1Error, NULL); } i2a_ASN1_OBJECT(bio, obj); ASN1_OBJECT_free(obj); ret = ossl_membio2str(bio); } return ret; } static VALUE decode_time(unsigned char* der, long length) { ASN1_TIME *time; const unsigned char *p; VALUE ret; int status = 0; p = der; if(!(time = d2i_ASN1_TIME(NULL, &p, length))) ossl_raise(eASN1Error, NULL); ret = rb_protect((VALUE (*)(VALUE))asn1time_to_time, (VALUE)time, &status); ASN1_TIME_free(time); if(status) rb_jump_tag(status); return ret; } static VALUE decode_eoc(unsigned char *der, long length) { if (length != 2 || !(der[0] == 0x00 && der[1] == 0x00)) ossl_raise(eASN1Error, NULL); return rb_str_new("", 0); } /********/ typedef struct { const char *name; VALUE *klass; } ossl_asn1_info_t; static const ossl_asn1_info_t ossl_asn1_info[] = { { "EOC", &cASN1EndOfContent, }, /* 0 */ { "BOOLEAN", &cASN1Boolean, }, /* 1 */ { "INTEGER", &cASN1Integer, }, /* 2 */ { "BIT_STRING", &cASN1BitString, }, /* 3 */ { "OCTET_STRING", &cASN1OctetString, }, /* 4 */ { "NULL", &cASN1Null, }, /* 5 */ { "OBJECT", &cASN1ObjectId, }, /* 6 */ { "OBJECT_DESCRIPTOR", NULL, }, /* 7 */ { "EXTERNAL", NULL, }, /* 8 */ { "REAL", NULL, }, /* 9 */ { "ENUMERATED", &cASN1Enumerated, }, /* 10 */ { "EMBEDDED_PDV", NULL, }, /* 11 */ { "UTF8STRING", &cASN1UTF8String, }, /* 12 */ { "RELATIVE_OID", NULL, }, /* 13 */ { "[UNIVERSAL 14]", NULL, }, /* 14 */ { "[UNIVERSAL 15]", NULL, }, /* 15 */ { "SEQUENCE", &cASN1Sequence, }, /* 16 */ { "SET", &cASN1Set, }, /* 17 */ { "NUMERICSTRING", &cASN1NumericString, }, /* 18 */ { "PRINTABLESTRING", &cASN1PrintableString, }, /* 19 */ { "T61STRING", &cASN1T61String, }, /* 20 */ { "VIDEOTEXSTRING", &cASN1VideotexString, }, /* 21 */ { "IA5STRING", &cASN1IA5String, }, /* 22 */ { "UTCTIME", &cASN1UTCTime, }, /* 23 */ { "GENERALIZEDTIME", &cASN1GeneralizedTime, }, /* 24 */ { "GRAPHICSTRING", &cASN1GraphicString, }, /* 25 */ { "ISO64STRING", &cASN1ISO64String, }, /* 26 */ { "GENERALSTRING", &cASN1GeneralString, }, /* 27 */ { "UNIVERSALSTRING", &cASN1UniversalString, }, /* 28 */ { "CHARACTER_STRING", NULL, }, /* 29 */ { "BMPSTRING", &cASN1BMPString, }, /* 30 */ }; enum {ossl_asn1_info_size = (sizeof(ossl_asn1_info)/sizeof(ossl_asn1_info[0]))}; static VALUE class_tag_map; static int ossl_asn1_default_tag(VALUE obj); ASN1_TYPE* ossl_asn1_get_asn1type(VALUE obj) { ASN1_TYPE *ret; VALUE value, rflag; void *ptr; void (*free_func)(); int tag, flag; tag = ossl_asn1_default_tag(obj); value = ossl_asn1_get_value(obj); switch(tag){ case V_ASN1_BOOLEAN: ptr = (void*)(VALUE)obj_to_asn1bool(value); free_func = NULL; break; case V_ASN1_INTEGER: /* FALLTHROUGH */ case V_ASN1_ENUMERATED: ptr = obj_to_asn1int(value); free_func = ASN1_INTEGER_free; break; case V_ASN1_BIT_STRING: rflag = rb_attr_get(obj, sivUNUSED_BITS); flag = NIL_P(rflag) ? -1 : NUM2INT(rflag); ptr = obj_to_asn1bstr(value, flag); free_func = ASN1_BIT_STRING_free; break; case V_ASN1_NULL: ptr = obj_to_asn1null(value); free_func = ASN1_NULL_free; break; case V_ASN1_OCTET_STRING: /* FALLTHROUGH */ case V_ASN1_UTF8STRING: /* FALLTHROUGH */ case V_ASN1_NUMERICSTRING: /* FALLTHROUGH */ case V_ASN1_PRINTABLESTRING: /* FALLTHROUGH */ case V_ASN1_T61STRING: /* FALLTHROUGH */ case V_ASN1_VIDEOTEXSTRING: /* FALLTHROUGH */ case V_ASN1_IA5STRING: /* FALLTHROUGH */ case V_ASN1_GRAPHICSTRING: /* FALLTHROUGH */ case V_ASN1_ISO64STRING: /* FALLTHROUGH */ case V_ASN1_GENERALSTRING: /* FALLTHROUGH */ case V_ASN1_UNIVERSALSTRING: /* FALLTHROUGH */ case V_ASN1_BMPSTRING: ptr = obj_to_asn1str(value); free_func = ASN1_STRING_free; break; case V_ASN1_OBJECT: ptr = obj_to_asn1obj(value); free_func = ASN1_OBJECT_free; break; case V_ASN1_UTCTIME: ptr = obj_to_asn1utime(value); free_func = ASN1_TIME_free; break; case V_ASN1_GENERALIZEDTIME: ptr = obj_to_asn1gtime(value); free_func = ASN1_TIME_free; break; case V_ASN1_SET: /* FALLTHROUGH */ case V_ASN1_SEQUENCE: ptr = obj_to_asn1derstr(obj); free_func = ASN1_STRING_free; break; default: ossl_raise(eASN1Error, "unsupported ASN.1 type"); } if(!(ret = OPENSSL_malloc(sizeof(ASN1_TYPE)))){ if(free_func) free_func(ptr); ossl_raise(eASN1Error, "ASN1_TYPE alloc failure"); } memset(ret, 0, sizeof(ASN1_TYPE)); ASN1_TYPE_set(ret, tag, ptr); return ret; } static int ossl_asn1_default_tag(VALUE obj) { VALUE tmp_class, tag; tmp_class = CLASS_OF(obj); while (!NIL_P(tmp_class)) { tag = rb_hash_lookup(class_tag_map, tmp_class); if (tag != Qnil) return NUM2INT(tag); tmp_class = rb_class_superclass(tmp_class); } ossl_raise(eASN1Error, "universal tag for %"PRIsVALUE" not found", rb_obj_class(obj)); } static int ossl_asn1_tag(VALUE obj) { VALUE tag; tag = ossl_asn1_get_tag(obj); if(NIL_P(tag)) ossl_raise(eASN1Error, "tag number not specified"); return NUM2INT(tag); } static int ossl_asn1_is_explicit(VALUE obj) { VALUE s; s = ossl_asn1_get_tagging(obj); if (NIL_P(s) || s == sym_IMPLICIT) return 0; else if (s == sym_EXPLICIT) return 1; else ossl_raise(eASN1Error, "invalid tag default"); } static int ossl_asn1_tag_class(VALUE obj) { VALUE s; s = ossl_asn1_get_tag_class(obj); if (NIL_P(s) || s == sym_UNIVERSAL) return V_ASN1_UNIVERSAL; else if (s == sym_APPLICATION) return V_ASN1_APPLICATION; else if (s == sym_CONTEXT_SPECIFIC) return V_ASN1_CONTEXT_SPECIFIC; else if (s == sym_PRIVATE) return V_ASN1_PRIVATE; else ossl_raise(eASN1Error, "invalid tag class"); } static VALUE ossl_asn1_class2sym(int tc) { if((tc & V_ASN1_PRIVATE) == V_ASN1_PRIVATE) return sym_PRIVATE; else if((tc & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC) return sym_CONTEXT_SPECIFIC; else if((tc & V_ASN1_APPLICATION) == V_ASN1_APPLICATION) return sym_APPLICATION; else return sym_UNIVERSAL; } /* * call-seq: * OpenSSL::ASN1::ASN1Data.new(value, tag, tag_class) => ASN1Data * * +value+: Please have a look at Constructive and Primitive to see how Ruby * types are mapped to ASN.1 types and vice versa. * * +tag+: A +Number+ indicating the tag number. * * +tag_class+: A +Symbol+ indicating the tag class. Please cf. ASN1 for * possible values. * * == Example * asn1_int = OpenSSL::ASN1Data.new(42, 2, :UNIVERSAL) # => Same as OpenSSL::ASN1::Integer.new(42) * tagged_int = OpenSSL::ASN1Data.new(42, 0, :CONTEXT_SPECIFIC) # implicitly 0-tagged INTEGER */ static VALUE ossl_asn1data_initialize(VALUE self, VALUE value, VALUE tag, VALUE tag_class) { if(!SYMBOL_P(tag_class)) ossl_raise(eASN1Error, "invalid tag class"); if (tag_class == sym_UNIVERSAL && NUM2INT(tag) > 31) ossl_raise(eASN1Error, "tag number for Universal too large"); ossl_asn1_set_tag(self, tag); ossl_asn1_set_value(self, value); ossl_asn1_set_tag_class(self, tag_class); ossl_asn1_set_infinite_length(self, Qfalse); return self; } static VALUE join_der_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, str)) { i = ossl_to_der_if_possible(i); StringValue(i); rb_str_append(str, i); return Qnil; } static VALUE join_der(VALUE enumerable) { VALUE str = rb_str_new(0, 0); rb_block_call(enumerable, id_each, 0, 0, join_der_i, str); return str; } /* * call-seq: * asn1.to_der => DER-encoded String * * Encodes this ASN1Data into a DER-encoded String value. The result is * DER-encoded except for the possibility of infinite length encodings. * Infinite length encodings are not allowed in strict DER, so strictly * speaking the result of such an encoding would be a BER-encoding. */ static VALUE ossl_asn1data_to_der(VALUE self) { VALUE value, der, inf_length; int tag, tag_class, is_cons = 0; long length; unsigned char *p; value = ossl_asn1_get_value(self); if(rb_obj_is_kind_of(value, rb_cArray)){ is_cons = 1; value = join_der(value); } StringValue(value); tag = ossl_asn1_tag(self); tag_class = ossl_asn1_tag_class(self); inf_length = ossl_asn1_get_infinite_length(self); if (inf_length == Qtrue) { is_cons = 2; } if((length = ASN1_object_size(is_cons, RSTRING_LENINT(value), tag)) <= 0) ossl_raise(eASN1Error, NULL); der = rb_str_new(0, length); p = (unsigned char *)RSTRING_PTR(der); ASN1_put_object(&p, is_cons, RSTRING_LENINT(value), tag, tag_class); memcpy(p, RSTRING_PTR(value), RSTRING_LEN(value)); p += RSTRING_LEN(value); ossl_str_adjust(der, p); return der; } static VALUE int_ossl_asn1_decode0_prim(unsigned char **pp, long length, long hlen, int tag, VALUE tc, long *num_read) { VALUE value, asn1data; unsigned char *p; long flag = 0; p = *pp; if(tc == sym_UNIVERSAL && tag < ossl_asn1_info_size) { switch(tag){ case V_ASN1_EOC: value = decode_eoc(p, hlen+length); break; case V_ASN1_BOOLEAN: value = decode_bool(p, hlen+length); break; case V_ASN1_INTEGER: value = decode_int(p, hlen+length); break; case V_ASN1_BIT_STRING: value = decode_bstr(p, hlen+length, &flag); break; case V_ASN1_NULL: value = decode_null(p, hlen+length); break; case V_ASN1_ENUMERATED: value = decode_enum(p, hlen+length); break; case V_ASN1_OBJECT: value = decode_obj(p, hlen+length); break; case V_ASN1_UTCTIME: /* FALLTHROUGH */ case V_ASN1_GENERALIZEDTIME: value = decode_time(p, hlen+length); break; default: /* use original value */ p += hlen; value = rb_str_new((const char *)p, length); break; } } else { p += hlen; value = rb_str_new((const char *)p, length); } *pp += hlen + length; *num_read = hlen + length; if (tc == sym_UNIVERSAL && tag < ossl_asn1_info_size && ossl_asn1_info[tag].klass) { VALUE klass = *ossl_asn1_info[tag].klass; VALUE args[4]; args[0] = value; args[1] = INT2NUM(tag); args[2] = Qnil; args[3] = tc; asn1data = rb_obj_alloc(klass); ossl_asn1_initialize(4, args, asn1data); if(tag == V_ASN1_BIT_STRING){ rb_ivar_set(asn1data, sivUNUSED_BITS, LONG2NUM(flag)); } } else { asn1data = rb_obj_alloc(cASN1Data); ossl_asn1data_initialize(asn1data, value, INT2NUM(tag), tc); } return asn1data; } static VALUE int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, long *offset, int depth, int yield, int j, int tag, VALUE tc, long *num_read) { VALUE value, asn1data, ary; int infinite; long available_len, off = *offset; infinite = (j == 0x21); ary = rb_ary_new(); available_len = infinite ? max_len : length; while (available_len > 0) { long inner_read = 0; value = ossl_asn1_decode0(pp, available_len, &off, depth + 1, yield, &inner_read); *num_read += inner_read; available_len -= inner_read; rb_ary_push(ary, value); if (infinite && NUM2INT(ossl_asn1_get_tag(value)) == V_ASN1_EOC && ossl_asn1_get_tag_class(value) == sym_UNIVERSAL) { break; } } if (tc == sym_UNIVERSAL) { VALUE args[4]; int not_sequence_or_set; not_sequence_or_set = tag != V_ASN1_SEQUENCE && tag != V_ASN1_SET; if (not_sequence_or_set) { if (infinite) { asn1data = rb_obj_alloc(cASN1Constructive); } else { ossl_raise(eASN1Error, "invalid non-infinite tag"); return Qnil; } } else { VALUE klass = *ossl_asn1_info[tag].klass; asn1data = rb_obj_alloc(klass); } args[0] = ary; args[1] = INT2NUM(tag); args[2] = Qnil; args[3] = tc; ossl_asn1_initialize(4, args, asn1data); } else { asn1data = rb_obj_alloc(cASN1Data); ossl_asn1data_initialize(asn1data, ary, INT2NUM(tag), tc); } if (infinite) ossl_asn1_set_infinite_length(asn1data, Qtrue); else ossl_asn1_set_infinite_length(asn1data, Qfalse); *offset = off; return asn1data; } static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth, int yield, long *num_read) { unsigned char *start, *p; const unsigned char *p0; long len = 0, inner_read = 0, off = *offset, hlen; int tag, tc, j; VALUE asn1data, tag_class; p = *pp; start = p; p0 = p; j = ASN1_get_object(&p0, &len, &tag, &tc, length); p = (unsigned char *)p0; if(j & 0x80) ossl_raise(eASN1Error, NULL); if(len > length) ossl_raise(eASN1Error, "value is too short"); if((tc & V_ASN1_PRIVATE) == V_ASN1_PRIVATE) tag_class = sym_PRIVATE; else if((tc & V_ASN1_CONTEXT_SPECIFIC) == V_ASN1_CONTEXT_SPECIFIC) tag_class = sym_CONTEXT_SPECIFIC; else if((tc & V_ASN1_APPLICATION) == V_ASN1_APPLICATION) tag_class = sym_APPLICATION; else tag_class = sym_UNIVERSAL; hlen = p - start; if(yield) { VALUE arg = rb_ary_new(); rb_ary_push(arg, LONG2NUM(depth)); rb_ary_push(arg, LONG2NUM(*offset)); rb_ary_push(arg, LONG2NUM(hlen)); rb_ary_push(arg, LONG2NUM(len)); rb_ary_push(arg, (j & V_ASN1_CONSTRUCTED) ? Qtrue : Qfalse); rb_ary_push(arg, ossl_asn1_class2sym(tc)); rb_ary_push(arg, INT2NUM(tag)); rb_yield(arg); } if(j & V_ASN1_CONSTRUCTED) { *pp += hlen; off += hlen; asn1data = int_ossl_asn1_decode0_cons(pp, length - hlen, len, &off, depth, yield, j, tag, tag_class, &inner_read); inner_read += hlen; } else { if ((j & 0x01) && (len == 0)) ossl_raise(eASN1Error, "Infinite length for primitive value"); asn1data = int_ossl_asn1_decode0_prim(pp, len, hlen, tag, tag_class, &inner_read); off += hlen + len; } if (num_read) *num_read = inner_read; if (len != 0 && inner_read != hlen + len) { ossl_raise(eASN1Error, "Type mismatch. Bytes read: %ld Bytes available: %ld", inner_read, hlen + len); } *offset = off; return asn1data; } static void int_ossl_decode_sanity_check(long len, long read, long offset) { if (len != 0 && (read != len || offset != len)) { ossl_raise(eASN1Error, "Type mismatch. Total bytes read: %ld Bytes available: %ld Offset: %ld", read, len, offset); } } /* * call-seq: * OpenSSL::ASN1.traverse(asn1) -> nil * * If a block is given, it prints out each of the elements encountered. * Block parameters are (in that order): * * depth: The recursion depth, plus one with each constructed value being encountered (Number) * * offset: Current byte offset (Number) * * header length: Combined length in bytes of the Tag and Length headers. (Number) * * length: The overall remaining length of the entire data (Number) * * constructed: Whether this value is constructed or not (Boolean) * * tag_class: Current tag class (Symbol) * * tag: The current tag (Number) * * == Example * der = File.binread('asn1data.der') * OpenSSL::ASN1.traverse(der) do | depth, offset, header_len, length, constructed, tag_class, tag| * puts "Depth: #{depth} Offset: #{offset} Length: #{length}" * puts "Header length: #{header_len} Tag: #{tag} Tag class: #{tag_class} Constructed: #{constructed}" * end */ static VALUE ossl_asn1_traverse(VALUE self, VALUE obj) { unsigned char *p; VALUE tmp; long len, read = 0, offset = 0; obj = ossl_to_der_if_possible(obj); tmp = rb_str_new4(StringValue(obj)); p = (unsigned char *)RSTRING_PTR(tmp); len = RSTRING_LEN(tmp); ossl_asn1_decode0(&p, len, &offset, 0, 1, &read); RB_GC_GUARD(tmp); int_ossl_decode_sanity_check(len, read, offset); return Qnil; } /* * call-seq: * OpenSSL::ASN1.decode(der) -> ASN1Data * * Decodes a BER- or DER-encoded value and creates an ASN1Data instance. +der+ * may be a +String+ or any object that features a +#to_der+ method transforming * it into a BER-/DER-encoded +String+. * * == Example * der = File.binread('asn1data') * asn1 = OpenSSL::ASN1.decode(der) */ static VALUE ossl_asn1_decode(VALUE self, VALUE obj) { VALUE ret; unsigned char *p; VALUE tmp; long len, read = 0, offset = 0; obj = ossl_to_der_if_possible(obj); tmp = rb_str_new4(StringValue(obj)); p = (unsigned char *)RSTRING_PTR(tmp); len = RSTRING_LEN(tmp); ret = ossl_asn1_decode0(&p, len, &offset, 0, 0, &read); RB_GC_GUARD(tmp); int_ossl_decode_sanity_check(len, read, offset); return ret; } /* * call-seq: * OpenSSL::ASN1.decode_all(der) -> Array of ASN1Data * * Similar to +decode+ with the difference that +decode+ expects one * distinct value represented in +der+. +decode_all+ on the contrary * decodes a sequence of sequential BER/DER values lined up in +der+ * and returns them as an array. * * == Example * ders = File.binread('asn1data_seq') * asn1_ary = OpenSSL::ASN1.decode_all(ders) */ static VALUE ossl_asn1_decode_all(VALUE self, VALUE obj) { VALUE ary, val; unsigned char *p; long len, tmp_len = 0, read = 0, offset = 0; VALUE tmp; obj = ossl_to_der_if_possible(obj); tmp = rb_str_new4(StringValue(obj)); p = (unsigned char *)RSTRING_PTR(tmp); len = RSTRING_LEN(tmp); tmp_len = len; ary = rb_ary_new(); while (tmp_len > 0) { long tmp_read = 0; val = ossl_asn1_decode0(&p, tmp_len, &offset, 0, 0, &tmp_read); rb_ary_push(ary, val); read += tmp_read; tmp_len -= tmp_read; } RB_GC_GUARD(tmp); int_ossl_decode_sanity_check(len, read, offset); return ary; } /* * call-seq: * OpenSSL::ASN1::Primitive.new( value [, tag, tagging, tag_class ]) => Primitive * * +value+: is mandatory. * * +tag+: optional, may be specified for tagged values. If no +tag+ is * specified, the UNIVERSAL tag corresponding to the Primitive sub-class * is used by default. * * +tagging+: may be used as an encoding hint to encode a value either * explicitly or implicitly, see ASN1 for possible values. * * +tag_class+: if +tag+ and +tagging+ are +nil+ then this is set to * +:UNIVERSAL+ by default. If either +tag+ or +tagging+ are set then * +:CONTEXT_SPECIFIC+ is used as the default. For possible values please * cf. ASN1. * * == Example * int = OpenSSL::ASN1::Integer.new(42) * zero_tagged_int = OpenSSL::ASN1::Integer.new(42, 0, :IMPLICIT) * private_explicit_zero_tagged_int = OpenSSL::ASN1::Integer.new(42, 0, :EXPLICIT, :PRIVATE) */ static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self) { VALUE value, tag, tagging, tag_class; rb_scan_args(argc, argv, "13", &value, &tag, &tagging, &tag_class); if(argc > 1){ if(NIL_P(tag)) ossl_raise(eASN1Error, "must specify tag number"); if(!NIL_P(tagging) && !SYMBOL_P(tagging)) ossl_raise(eASN1Error, "invalid tagging method"); if(NIL_P(tag_class)) { if (NIL_P(tagging)) tag_class = sym_UNIVERSAL; else tag_class = sym_CONTEXT_SPECIFIC; } if(!SYMBOL_P(tag_class)) ossl_raise(eASN1Error, "invalid tag class"); if (tagging == sym_IMPLICIT && NUM2INT(tag) > 31) ossl_raise(eASN1Error, "tag number for Universal too large"); } else{ tag = INT2NUM(ossl_asn1_default_tag(self)); tagging = Qnil; tag_class = sym_UNIVERSAL; } ossl_asn1_set_tag(self, tag); ossl_asn1_set_value(self, value); ossl_asn1_set_tagging(self, tagging); ossl_asn1_set_tag_class(self, tag_class); ossl_asn1_set_infinite_length(self, Qfalse); return self; } static VALUE ossl_asn1eoc_initialize(VALUE self) { VALUE tag, tagging, tag_class, value; tag = INT2NUM(ossl_asn1_default_tag(self)); tagging = Qnil; tag_class = sym_UNIVERSAL; value = rb_str_new("", 0); ossl_asn1_set_tag(self, tag); ossl_asn1_set_value(self, value); ossl_asn1_set_tagging(self, tagging); ossl_asn1_set_tag_class(self, tag_class); ossl_asn1_set_infinite_length(self, Qfalse); return self; } /* * call-seq: * asn1.to_der => DER-encoded String * * See ASN1Data#to_der for details. * */ static VALUE ossl_asn1prim_to_der(VALUE self) { ASN1_TYPE *asn1; int tn, tc, explicit; long len, reallen; unsigned char *buf, *p; VALUE str; tn = NUM2INT(ossl_asn1_get_tag(self)); tc = ossl_asn1_tag_class(self); explicit = ossl_asn1_is_explicit(self); asn1 = ossl_asn1_get_asn1type(self); len = ASN1_object_size(1, i2d_ASN1_TYPE(asn1, NULL), tn); if(!(buf = OPENSSL_malloc(len))){ ASN1_TYPE_free(asn1); ossl_raise(eASN1Error, "cannot alloc buffer"); } p = buf; if (tc == V_ASN1_UNIVERSAL) { i2d_ASN1_TYPE(asn1, &p); } else if (explicit) { ASN1_put_object(&p, 1, i2d_ASN1_TYPE(asn1, NULL), tn, tc); i2d_ASN1_TYPE(asn1, &p); } else { i2d_ASN1_TYPE(asn1, &p); *buf = tc | tn | (*buf & V_ASN1_CONSTRUCTED); } ASN1_TYPE_free(asn1); reallen = p - buf; assert(reallen <= len); str = ossl_buf2str((char *)buf, rb_long2int(reallen)); /* buf will be free in ossl_buf2str */ return str; } /* * call-seq: * asn1.to_der => DER-encoded String * * See ASN1Data#to_der for details. */ static VALUE ossl_asn1cons_to_der(VALUE self) { int tag, tn, tc, explicit, constructed = 1; int found_prim = 0, seq_len; long length; unsigned char *p; VALUE value, str, inf_length; tn = NUM2INT(ossl_asn1_get_tag(self)); tc = ossl_asn1_tag_class(self); inf_length = ossl_asn1_get_infinite_length(self); if (inf_length == Qtrue) { VALUE ary, example; constructed = 2; if (rb_obj_class(self) == cASN1Sequence || rb_obj_class(self) == cASN1Set) { tag = ossl_asn1_default_tag(self); } else { /* must be a constructive encoding of a primitive value */ ary = ossl_asn1_get_value(self); if (!rb_obj_is_kind_of(ary, rb_cArray)) ossl_raise(eASN1Error, "Constructive value must be an Array"); /* Recursively descend until a primitive value is found. The overall value of the entire constructed encoding is of the type of the first primitive encoding to be found. */ while (!found_prim){ example = rb_ary_entry(ary, 0); if (rb_obj_is_kind_of(example, cASN1Primitive)){ found_prim = 1; } else { /* example is another ASN1Constructive */ if (!rb_obj_is_kind_of(example, cASN1Constructive)){ ossl_raise(eASN1Error, "invalid constructed encoding"); return Qnil; /* dummy */ } ary = ossl_asn1_get_value(example); } } tag = ossl_asn1_default_tag(example); } } else { if (rb_obj_class(self) == cASN1Constructive) ossl_raise(eASN1Error, "Constructive shall only be used with infinite length"); tag = ossl_asn1_default_tag(self); } explicit = ossl_asn1_is_explicit(self); value = join_der(ossl_asn1_get_value(self)); seq_len = ASN1_object_size(constructed, RSTRING_LENINT(value), tag); length = ASN1_object_size(constructed, seq_len, tn); str = rb_str_new(0, length); p = (unsigned char *)RSTRING_PTR(str); if(tc == V_ASN1_UNIVERSAL) ASN1_put_object(&p, constructed, RSTRING_LENINT(value), tn, tc); else{ if(explicit){ ASN1_put_object(&p, constructed, seq_len, tn, tc); ASN1_put_object(&p, constructed, RSTRING_LENINT(value), tag, V_ASN1_UNIVERSAL); } else{ ASN1_put_object(&p, constructed, RSTRING_LENINT(value), tn, tc); } } memcpy(p, RSTRING_PTR(value), RSTRING_LEN(value)); p += RSTRING_LEN(value); /* In this case we need an additional EOC (one for the explicit part and * one for the Constructive itself. The EOC for the Constructive is * supplied by the user, but that for the "explicit wrapper" must be * added here. */ if (explicit && inf_length == Qtrue) { ASN1_put_eoc(&p); } ossl_str_adjust(str, p); return str; } /* * call-seq: * asn1_ary.each { |asn1| block } => asn1_ary * * Calls block once for each element in +self+, passing that element * as parameter +asn1+. If no block is given, an enumerator is returned * instead. * * == Example * asn1_ary.each do |asn1| * puts asn1 * end */ static VALUE ossl_asn1cons_each(VALUE self) { rb_block_call(ossl_asn1_get_value(self), id_each, 0, 0, 0, 0); return self; } /* * call-seq: * OpenSSL::ASN1::ObjectId.register(object_id, short_name, long_name) * * This adds a new ObjectId to the internal tables. Where +object_id+ is the * numerical form, +short_name+ is the short name, and +long_name+ is the long * name. * * Returns +true+ if successful. Raises an OpenSSL::ASN1::ASN1Error if it fails. * */ static VALUE ossl_asn1obj_s_register(VALUE self, VALUE oid, VALUE sn, VALUE ln) { StringValueCStr(oid); StringValueCStr(sn); StringValueCStr(ln); if(!OBJ_create(RSTRING_PTR(oid), RSTRING_PTR(sn), RSTRING_PTR(ln))) ossl_raise(eASN1Error, NULL); return Qtrue; } /* Document-method: OpenSSL::ASN1::ObjectId#sn * * The short name of the ObjectId, as defined in . */ /* Document-method: OpenSSL::ASN1::ObjectId#short_name * * +short_name+ is an alias to +sn+ */ static VALUE ossl_asn1obj_get_sn(VALUE self) { VALUE val, ret = Qnil; int nid; val = ossl_asn1_get_value(self); if ((nid = OBJ_txt2nid(StringValueCStr(val))) != NID_undef) ret = rb_str_new2(OBJ_nid2sn(nid)); return ret; } /* Document-method: OpenSSL::ASN1::ObjectId#ln * * The long name of the ObjectId, as defined in . */ /* Document-method: OpenSSL::ASN1::ObjectId#long_name * * +long_name+ is an alias to +ln+ */ static VALUE ossl_asn1obj_get_ln(VALUE self) { VALUE val, ret = Qnil; int nid; val = ossl_asn1_get_value(self); if ((nid = OBJ_txt2nid(StringValueCStr(val))) != NID_undef) ret = rb_str_new2(OBJ_nid2ln(nid)); return ret; } /* Document-method: OpenSSL::ASN1::ObjectId#oid * * The object identifier as a +String+, e.g. "1.2.3.4.5" */ static VALUE ossl_asn1obj_get_oid(VALUE self) { VALUE val; ASN1_OBJECT *a1obj; char buf[128]; val = ossl_asn1_get_value(self); a1obj = obj_to_asn1obj(val); OBJ_obj2txt(buf, sizeof(buf), a1obj, 1); ASN1_OBJECT_free(a1obj); return rb_str_new2(buf); } #define OSSL_ASN1_IMPL_FACTORY_METHOD(klass) \ static VALUE ossl_asn1_##klass(int argc, VALUE *argv, VALUE self)\ { return rb_funcall3(cASN1##klass, rb_intern("new"), argc, argv); } OSSL_ASN1_IMPL_FACTORY_METHOD(Boolean) OSSL_ASN1_IMPL_FACTORY_METHOD(Integer) OSSL_ASN1_IMPL_FACTORY_METHOD(Enumerated) OSSL_ASN1_IMPL_FACTORY_METHOD(BitString) OSSL_ASN1_IMPL_FACTORY_METHOD(OctetString) OSSL_ASN1_IMPL_FACTORY_METHOD(UTF8String) OSSL_ASN1_IMPL_FACTORY_METHOD(NumericString) OSSL_ASN1_IMPL_FACTORY_METHOD(PrintableString) OSSL_ASN1_IMPL_FACTORY_METHOD(T61String) OSSL_ASN1_IMPL_FACTORY_METHOD(VideotexString) OSSL_ASN1_IMPL_FACTORY_METHOD(IA5String) OSSL_ASN1_IMPL_FACTORY_METHOD(GraphicString) OSSL_ASN1_IMPL_FACTORY_METHOD(ISO64String) OSSL_ASN1_IMPL_FACTORY_METHOD(GeneralString) OSSL_ASN1_IMPL_FACTORY_METHOD(UniversalString) OSSL_ASN1_IMPL_FACTORY_METHOD(BMPString) OSSL_ASN1_IMPL_FACTORY_METHOD(Null) OSSL_ASN1_IMPL_FACTORY_METHOD(ObjectId) OSSL_ASN1_IMPL_FACTORY_METHOD(UTCTime) OSSL_ASN1_IMPL_FACTORY_METHOD(GeneralizedTime) OSSL_ASN1_IMPL_FACTORY_METHOD(Sequence) OSSL_ASN1_IMPL_FACTORY_METHOD(Set) OSSL_ASN1_IMPL_FACTORY_METHOD(EndOfContent) void Init_ossl_asn1(void) { #undef rb_intern VALUE ary; int i; #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif sym_UNIVERSAL = ID2SYM(rb_intern_const("UNIVERSAL")); sym_CONTEXT_SPECIFIC = ID2SYM(rb_intern_const("CONTEXT_SPECIFIC")); sym_APPLICATION = ID2SYM(rb_intern_const("APPLICATION")); sym_PRIVATE = ID2SYM(rb_intern_const("PRIVATE")); sym_EXPLICIT = ID2SYM(rb_intern_const("EXPLICIT")); sym_IMPLICIT = ID2SYM(rb_intern_const("IMPLICIT")); sivVALUE = rb_intern("@value"); sivTAG = rb_intern("@tag"); sivTAGGING = rb_intern("@tagging"); sivTAG_CLASS = rb_intern("@tag_class"); sivINFINITE_LENGTH = rb_intern("@infinite_length"); sivUNUSED_BITS = rb_intern("@unused_bits"); /* * Document-module: OpenSSL::ASN1 * * Abstract Syntax Notation One (or ASN.1) is a notation syntax to * describe data structures and is defined in ITU-T X.680. ASN.1 itself * does not mandate any encoding or parsing rules, but usually ASN.1 data * structures are encoded using the Distinguished Encoding Rules (DER) or * less often the Basic Encoding Rules (BER) described in ITU-T X.690. DER * and BER encodings are binary Tag-Length-Value (TLV) encodings that are * quite concise compared to other popular data description formats such * as XML, JSON etc. * ASN.1 data structures are very common in cryptographic applications, * e.g. X.509 public key certificates or certificate revocation lists * (CRLs) are all defined in ASN.1 and DER-encoded. ASN.1, DER and BER are * the building blocks of applied cryptography. * The ASN1 module provides the necessary classes that allow generation * of ASN.1 data structures and the methods to encode them using a DER * encoding. The decode method allows parsing arbitrary BER-/DER-encoded * data to a Ruby object that can then be modified and re-encoded at will. * * == ASN.1 class hierarchy * * The base class representing ASN.1 structures is ASN1Data. ASN1Data offers * attributes to read and set the +tag+, the +tag_class+ and finally the * +value+ of a particular ASN.1 item. Upon parsing, any tagged values * (implicit or explicit) will be represented by ASN1Data instances because * their "real type" can only be determined using out-of-band information * from the ASN.1 type declaration. Since this information is normally * known when encoding a type, all sub-classes of ASN1Data offer an * additional attribute +tagging+ that allows to encode a value implicitly * (+:IMPLICIT+) or explicitly (+:EXPLICIT+). * * === Constructive * * Constructive is, as its name implies, the base class for all * constructed encodings, i.e. those that consist of several values, * opposed to "primitive" encodings with just one single value. * Primitive values that are encoded with "infinite length" are typically * constructed (their values come in multiple chunks) and are therefore * represented by instances of Constructive. The value of an Constructive * is always an Array. * * ==== ASN1::Set and ASN1::Sequence * * The most common constructive encodings are SETs and SEQUENCEs, which is * why there are two sub-classes of Constructive representing each of * them. * * === Primitive * * This is the super class of all primitive values. Primitive * itself is not used when parsing ASN.1 data, all values are either * instances of a corresponding sub-class of Primitive or they are * instances of ASN1Data if the value was tagged implicitly or explicitly. * Please cf. Primitive documentation for details on sub-classes and * their respective mappings of ASN.1 data types to Ruby objects. * * == Possible values for +tagging+ * * When constructing an ASN1Data object the ASN.1 type definition may * require certain elements to be either implicitly or explicitly tagged. * This can be achieved by setting the +tagging+ attribute manually for * sub-classes of ASN1Data. Use the symbol +:IMPLICIT+ for implicit * tagging and +:EXPLICIT+ if the element requires explicit tagging. * * == Possible values for +tag_class+ * * It is possible to create arbitrary ASN1Data objects that also support * a PRIVATE or APPLICATION tag class. Possible values for the +tag_class+ * attribute are: * * +:UNIVERSAL+ (the default for untagged values) * * +:CONTEXT_SPECIFIC+ (the default for tagged values) * * +:APPLICATION+ * * +:PRIVATE+ * * == Tag constants * * There is a constant defined for each universal tag: * * OpenSSL::ASN1::EOC (0) * * OpenSSL::ASN1::BOOLEAN (1) * * OpenSSL::ASN1::INTEGER (2) * * OpenSSL::ASN1::BIT_STRING (3) * * OpenSSL::ASN1::OCTET_STRING (4) * * OpenSSL::ASN1::NULL (5) * * OpenSSL::ASN1::OBJECT (6) * * OpenSSL::ASN1::ENUMERATED (10) * * OpenSSL::ASN1::UTF8STRING (12) * * OpenSSL::ASN1::SEQUENCE (16) * * OpenSSL::ASN1::SET (17) * * OpenSSL::ASN1::NUMERICSTRING (18) * * OpenSSL::ASN1::PRINTABLESTRING (19) * * OpenSSL::ASN1::T61STRING (20) * * OpenSSL::ASN1::VIDEOTEXSTRING (21) * * OpenSSL::ASN1::IA5STRING (22) * * OpenSSL::ASN1::UTCTIME (23) * * OpenSSL::ASN1::GENERALIZEDTIME (24) * * OpenSSL::ASN1::GRAPHICSTRING (25) * * OpenSSL::ASN1::ISO64STRING (26) * * OpenSSL::ASN1::GENERALSTRING (27) * * OpenSSL::ASN1::UNIVERSALSTRING (28) * * OpenSSL::ASN1::BMPSTRING (30) * * == UNIVERSAL_TAG_NAME constant * * An Array that stores the name of a given tag number. These names are * the same as the name of the tag constant that is additionally defined, * e.g. UNIVERSAL_TAG_NAME[2] = "INTEGER" and OpenSSL::ASN1::INTEGER = 2. * * == Example usage * * === Decoding and viewing a DER-encoded file * require 'openssl' * require 'pp' * der = File.binread('data.der') * asn1 = OpenSSL::ASN1.decode(der) * pp der * * === Creating an ASN.1 structure and DER-encoding it * require 'openssl' * version = OpenSSL::ASN1::Integer.new(1) * # Explicitly 0-tagged implies context-specific tag class * serial = OpenSSL::ASN1::Integer.new(12345, 0, :EXPLICIT, :CONTEXT_SPECIFIC) * name = OpenSSL::ASN1::PrintableString.new('Data 1') * sequence = OpenSSL::ASN1::Sequence.new( [ version, serial, name ] ) * der = sequence.to_der */ mASN1 = rb_define_module_under(mOSSL, "ASN1"); /* Document-class: OpenSSL::ASN1::ASN1Error * * Generic error class for all errors raised in ASN1 and any of the * classes defined in it. */ eASN1Error = rb_define_class_under(mASN1, "ASN1Error", eOSSLError); rb_define_module_function(mASN1, "traverse", ossl_asn1_traverse, 1); rb_define_module_function(mASN1, "decode", ossl_asn1_decode, 1); rb_define_module_function(mASN1, "decode_all", ossl_asn1_decode_all, 1); ary = rb_ary_new(); /* * Array storing tag names at the tag's index. */ rb_define_const(mASN1, "UNIVERSAL_TAG_NAME", ary); for(i = 0; i < ossl_asn1_info_size; i++){ if(ossl_asn1_info[i].name[0] == '[') continue; rb_define_const(mASN1, ossl_asn1_info[i].name, INT2NUM(i)); rb_ary_store(ary, i, rb_str_new2(ossl_asn1_info[i].name)); } /* Document-class: OpenSSL::ASN1::ASN1Data * * The top-level class representing any ASN.1 object. When parsed by * ASN1.decode, tagged values are always represented by an instance * of ASN1Data. * * == The role of ASN1Data for parsing tagged values * * When encoding an ASN.1 type it is inherently clear what original * type (e.g. INTEGER, OCTET STRING etc.) this value has, regardless * of its tagging. * But opposed to the time an ASN.1 type is to be encoded, when parsing * them it is not possible to deduce the "real type" of tagged * values. This is why tagged values are generally parsed into ASN1Data * instances, but with a different outcome for implicit and explicit * tagging. * * === Example of a parsed implicitly tagged value * * An implicitly 1-tagged INTEGER value will be parsed as an * ASN1Data with * * +tag+ equal to 1 * * +tag_class+ equal to +:CONTEXT_SPECIFIC+ * * +value+ equal to a +String+ that carries the raw encoding * of the INTEGER. * This implies that a subsequent decoding step is required to * completely decode implicitly tagged values. * * === Example of a parsed explicitly tagged value * * An explicitly 1-tagged INTEGER value will be parsed as an * ASN1Data with * * +tag+ equal to 1 * * +tag_class+ equal to +:CONTEXT_SPECIFIC+ * * +value+ equal to an +Array+ with one single element, an * instance of OpenSSL::ASN1::Integer, i.e. the inner element * is the non-tagged primitive value, and the tagging is represented * in the outer ASN1Data * * == Example - Decoding an implicitly tagged INTEGER * int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) # implicit 0-tagged * seq = OpenSSL::ASN1::Sequence.new( [int] ) * der = seq.to_der * asn1 = OpenSSL::ASN1.decode(der) * # pp asn1 => #]> * raw_int = asn1.value[0] * # manually rewrite tag and tag class to make it an UNIVERSAL value * raw_int.tag = OpenSSL::ASN1::INTEGER * raw_int.tag_class = :UNIVERSAL * int2 = OpenSSL::ASN1.decode(raw_int) * puts int2.value # => 1 * * == Example - Decoding an explicitly tagged INTEGER * int = OpenSSL::ASN1::Integer.new(1, 0, :EXPLICIT) # explicit 0-tagged * seq = OpenSSL::ASN1::Sequence.new( [int] ) * der = seq.to_der * asn1 = OpenSSL::ASN1.decode(der) * # pp asn1 => #]>]> * int2 = asn1.value[0].value[0] * puts int2.value # => 1 */ cASN1Data = rb_define_class_under(mASN1, "ASN1Data", rb_cObject); /* * Carries the value of a ASN.1 type. * Please confer Constructive and Primitive for the mappings between * ASN.1 data types and Ruby classes. */ rb_attr(cASN1Data, rb_intern("value"), 1, 1, 0); /* * A +Number+ representing the tag number of this ASN1Data. Never +nil+. */ rb_attr(cASN1Data, rb_intern("tag"), 1, 1, 0); /* * A +Symbol+ representing the tag class of this ASN1Data. Never +nil+. * See ASN1Data for possible values. */ rb_attr(cASN1Data, rb_intern("tag_class"), 1, 1, 0); /* * Never +nil+. A +Boolean+ indicating whether the encoding was infinite * length (in the case of parsing) or whether an infinite length encoding * shall be used (in the encoding case). * In DER, every value has a finite length associated with it. But in * scenarios where large amounts of data need to be transferred it * might be desirable to have some kind of streaming support available. * For example, huge OCTET STRINGs are preferably sent in smaller-sized * chunks, each at a time. * This is possible in BER by setting the length bytes of an encoding * to zero and by this indicating that the following value will be * sent in chunks. Infinite length encodings are always constructed. * The end of such a stream of chunks is indicated by sending a EOC * (End of Content) tag. SETs and SEQUENCEs may use an infinite length * encoding, but also primitive types such as e.g. OCTET STRINGS or * BIT STRINGS may leverage this functionality (cf. ITU-T X.690). */ rb_attr(cASN1Data, rb_intern("infinite_length"), 1, 1, 0); rb_define_method(cASN1Data, "initialize", ossl_asn1data_initialize, 3); rb_define_method(cASN1Data, "to_der", ossl_asn1data_to_der, 0); /* Document-class: OpenSSL::ASN1::Primitive * * The parent class for all primitive encodings. Attributes are the same as * for ASN1Data, with the addition of +tagging+. * Primitive values can never be infinite length encodings, thus it is not * possible to set the +infinite_length+ attribute for Primitive and its * sub-classes. * * == Primitive sub-classes and their mapping to Ruby classes * * OpenSSL::ASN1::EndOfContent <=> +value+ is always +nil+ * * OpenSSL::ASN1::Boolean <=> +value+ is a +Boolean+ * * OpenSSL::ASN1::Integer <=> +value+ is an OpenSSL::BN * * OpenSSL::ASN1::BitString <=> +value+ is a +String+ * * OpenSSL::ASN1::OctetString <=> +value+ is a +String+ * * OpenSSL::ASN1::Null <=> +value+ is always +nil+ * * OpenSSL::ASN1::Object <=> +value+ is a +String+ * * OpenSSL::ASN1::Enumerated <=> +value+ is an OpenSSL::BN * * OpenSSL::ASN1::UTF8String <=> +value+ is a +String+ * * OpenSSL::ASN1::NumericString <=> +value+ is a +String+ * * OpenSSL::ASN1::PrintableString <=> +value+ is a +String+ * * OpenSSL::ASN1::T61String <=> +value+ is a +String+ * * OpenSSL::ASN1::VideotexString <=> +value+ is a +String+ * * OpenSSL::ASN1::IA5String <=> +value+ is a +String+ * * OpenSSL::ASN1::UTCTime <=> +value+ is a +Time+ * * OpenSSL::ASN1::GeneralizedTime <=> +value+ is a +Time+ * * OpenSSL::ASN1::GraphicString <=> +value+ is a +String+ * * OpenSSL::ASN1::ISO64String <=> +value+ is a +String+ * * OpenSSL::ASN1::GeneralString <=> +value+ is a +String+ * * OpenSSL::ASN1::UniversalString <=> +value+ is a +String+ * * OpenSSL::ASN1::BMPString <=> +value+ is a +String+ * * == OpenSSL::ASN1::BitString * * === Additional attributes * +unused_bits+: if the underlying BIT STRING's * length is a multiple of 8 then +unused_bits+ is 0. Otherwise * +unused_bits+ indicates the number of bits that are to be ignored in * the final octet of the +BitString+'s +value+. * * == OpenSSL::ASN1::ObjectId * * NOTE: While OpenSSL::ASN1::ObjectId.new will allocate a new ObjectId, * it is not typically allocated this way, but rather that are received from * parsed ASN1 encodings. * * === Additional attributes * * +sn+: the short name as defined in . * * +ln+: the long name as defined in . * * +oid+: the object identifier as a +String+, e.g. "1.2.3.4.5" * * +short_name+: alias for +sn+. * * +long_name+: alias for +ln+. * * == Examples * With the Exception of OpenSSL::ASN1::EndOfContent, each Primitive class * constructor takes at least one parameter, the +value+. * * === Creating EndOfContent * eoc = OpenSSL::ASN1::EndOfContent.new * * === Creating any other Primitive * prim = .new(value) # being one of the sub-classes except EndOfContent * prim_zero_tagged_implicit = .new(value, 0, :IMPLICIT) * prim_zero_tagged_explicit = .new(value, 0, :EXPLICIT) */ cASN1Primitive = rb_define_class_under(mASN1, "Primitive", cASN1Data); /* * May be used as a hint for encoding a value either implicitly or * explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+. * +tagging+ is not set when a ASN.1 structure is parsed using * OpenSSL::ASN1.decode. */ rb_attr(cASN1Primitive, rb_intern("tagging"), 1, 1, Qtrue); rb_undef_method(cASN1Primitive, "infinite_length="); rb_define_method(cASN1Primitive, "initialize", ossl_asn1_initialize, -1); rb_define_method(cASN1Primitive, "to_der", ossl_asn1prim_to_der, 0); /* Document-class: OpenSSL::ASN1::Constructive * * The parent class for all constructed encodings. The +value+ attribute * of a Constructive is always an +Array+. Attributes are the same as * for ASN1Data, with the addition of +tagging+. * * == SET and SEQUENCE * * Most constructed encodings come in the form of a SET or a SEQUENCE. * These encodings are represented by one of the two sub-classes of * Constructive: * * OpenSSL::ASN1::Set * * OpenSSL::ASN1::Sequence * Please note that tagged sequences and sets are still parsed as * instances of ASN1Data. Find further details on tagged values * there. * * === Example - constructing a SEQUENCE * int = OpenSSL::ASN1::Integer.new(1) * str = OpenSSL::ASN1::PrintableString.new('abc') * sequence = OpenSSL::ASN1::Sequence.new( [ int, str ] ) * * === Example - constructing a SET * int = OpenSSL::ASN1::Integer.new(1) * str = OpenSSL::ASN1::PrintableString.new('abc') * set = OpenSSL::ASN1::Set.new( [ int, str ] ) * * == Infinite length primitive values * * The only case where Constructive is used directly is for infinite * length encodings of primitive values. These encodings are always * constructed, with the contents of the +value+ +Array+ being either * UNIVERSAL non-infinite length partial encodings of the actual value * or again constructive encodings with infinite length (i.e. infinite * length primitive encodings may be constructed recursively with another * infinite length value within an already infinite length value). Each * partial encoding must be of the same UNIVERSAL type as the overall * encoding. The value of the overall encoding consists of the * concatenation of each partial encoding taken in sequence. The +value+ * array of the outer infinite length value must end with a * OpenSSL::ASN1::EndOfContent instance. * * Please note that it is not possible to encode Constructive without * the +infinite_length+ attribute being set to +true+, use * OpenSSL::ASN1::Sequence or OpenSSL::ASN1::Set in these cases instead. * * === Example - Infinite length OCTET STRING * partial1 = OpenSSL::ASN1::OctetString.new("\x01") * partial2 = OpenSSL::ASN1::OctetString.new("\x02") * inf_octets = OpenSSL::ASN1::Constructive.new( [ partial1, * partial2, * OpenSSL::ASN1::EndOfContent.new ], * OpenSSL::ASN1::OCTET_STRING, * nil, * :UNIVERSAL ) * # The real value of inf_octets is "\x01\x02", i.e. the concatenation * # of partial1 and partial2 * inf_octets.infinite_length = true * der = inf_octets.to_der * asn1 = OpenSSL::ASN1.decode(der) * puts asn1.infinite_length # => true */ cASN1Constructive = rb_define_class_under(mASN1,"Constructive", cASN1Data); rb_include_module(cASN1Constructive, rb_mEnumerable); /* * May be used as a hint for encoding a value either implicitly or * explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+. * +tagging+ is not set when a ASN.1 structure is parsed using * OpenSSL::ASN1.decode. */ rb_attr(cASN1Constructive, rb_intern("tagging"), 1, 1, Qtrue); rb_define_method(cASN1Constructive, "initialize", ossl_asn1_initialize, -1); rb_define_method(cASN1Constructive, "to_der", ossl_asn1cons_to_der, 0); rb_define_method(cASN1Constructive, "each", ossl_asn1cons_each, 0); #define OSSL_ASN1_DEFINE_CLASS(name, super) \ do{\ cASN1##name = rb_define_class_under(mASN1, #name, cASN1##super);\ rb_define_module_function(mASN1, #name, ossl_asn1_##name, -1);\ }while(0) OSSL_ASN1_DEFINE_CLASS(Boolean, Primitive); OSSL_ASN1_DEFINE_CLASS(Integer, Primitive); OSSL_ASN1_DEFINE_CLASS(Enumerated, Primitive); OSSL_ASN1_DEFINE_CLASS(BitString, Primitive); OSSL_ASN1_DEFINE_CLASS(OctetString, Primitive); OSSL_ASN1_DEFINE_CLASS(UTF8String, Primitive); OSSL_ASN1_DEFINE_CLASS(NumericString, Primitive); OSSL_ASN1_DEFINE_CLASS(PrintableString, Primitive); OSSL_ASN1_DEFINE_CLASS(T61String, Primitive); OSSL_ASN1_DEFINE_CLASS(VideotexString, Primitive); OSSL_ASN1_DEFINE_CLASS(IA5String, Primitive); OSSL_ASN1_DEFINE_CLASS(GraphicString, Primitive); OSSL_ASN1_DEFINE_CLASS(ISO64String, Primitive); OSSL_ASN1_DEFINE_CLASS(GeneralString, Primitive); OSSL_ASN1_DEFINE_CLASS(UniversalString, Primitive); OSSL_ASN1_DEFINE_CLASS(BMPString, Primitive); OSSL_ASN1_DEFINE_CLASS(Null, Primitive); OSSL_ASN1_DEFINE_CLASS(ObjectId, Primitive); OSSL_ASN1_DEFINE_CLASS(UTCTime, Primitive); OSSL_ASN1_DEFINE_CLASS(GeneralizedTime, Primitive); OSSL_ASN1_DEFINE_CLASS(Sequence, Constructive); OSSL_ASN1_DEFINE_CLASS(Set, Constructive); OSSL_ASN1_DEFINE_CLASS(EndOfContent, Data); /* Document-class: OpenSSL::ASN1::ObjectId * * Represents the primitive object id for OpenSSL::ASN1 */ #if 0 cASN1ObjectId = rb_define_class_under(mASN1, "ObjectId", cASN1Primitive); /* let rdoc know */ #endif rb_define_singleton_method(cASN1ObjectId, "register", ossl_asn1obj_s_register, 3); rb_define_method(cASN1ObjectId, "sn", ossl_asn1obj_get_sn, 0); rb_define_method(cASN1ObjectId, "ln", ossl_asn1obj_get_ln, 0); rb_define_method(cASN1ObjectId, "oid", ossl_asn1obj_get_oid, 0); rb_define_alias(cASN1ObjectId, "short_name", "sn"); rb_define_alias(cASN1ObjectId, "long_name", "ln"); rb_attr(cASN1BitString, rb_intern("unused_bits"), 1, 1, 0); rb_define_method(cASN1EndOfContent, "initialize", ossl_asn1eoc_initialize, 0); class_tag_map = rb_hash_new(); rb_hash_aset(class_tag_map, cASN1EndOfContent, INT2NUM(V_ASN1_EOC)); rb_hash_aset(class_tag_map, cASN1Boolean, INT2NUM(V_ASN1_BOOLEAN)); rb_hash_aset(class_tag_map, cASN1Integer, INT2NUM(V_ASN1_INTEGER)); rb_hash_aset(class_tag_map, cASN1BitString, INT2NUM(V_ASN1_BIT_STRING)); rb_hash_aset(class_tag_map, cASN1OctetString, INT2NUM(V_ASN1_OCTET_STRING)); rb_hash_aset(class_tag_map, cASN1Null, INT2NUM(V_ASN1_NULL)); rb_hash_aset(class_tag_map, cASN1ObjectId, INT2NUM(V_ASN1_OBJECT)); rb_hash_aset(class_tag_map, cASN1Enumerated, INT2NUM(V_ASN1_ENUMERATED)); rb_hash_aset(class_tag_map, cASN1UTF8String, INT2NUM(V_ASN1_UTF8STRING)); rb_hash_aset(class_tag_map, cASN1Sequence, INT2NUM(V_ASN1_SEQUENCE)); rb_hash_aset(class_tag_map, cASN1Set, INT2NUM(V_ASN1_SET)); rb_hash_aset(class_tag_map, cASN1NumericString, INT2NUM(V_ASN1_NUMERICSTRING)); rb_hash_aset(class_tag_map, cASN1PrintableString, INT2NUM(V_ASN1_PRINTABLESTRING)); rb_hash_aset(class_tag_map, cASN1T61String, INT2NUM(V_ASN1_T61STRING)); rb_hash_aset(class_tag_map, cASN1VideotexString, INT2NUM(V_ASN1_VIDEOTEXSTRING)); rb_hash_aset(class_tag_map, cASN1IA5String, INT2NUM(V_ASN1_IA5STRING)); rb_hash_aset(class_tag_map, cASN1UTCTime, INT2NUM(V_ASN1_UTCTIME)); rb_hash_aset(class_tag_map, cASN1GeneralizedTime, INT2NUM(V_ASN1_GENERALIZEDTIME)); rb_hash_aset(class_tag_map, cASN1GraphicString, INT2NUM(V_ASN1_GRAPHICSTRING)); rb_hash_aset(class_tag_map, cASN1ISO64String, INT2NUM(V_ASN1_ISO64STRING)); rb_hash_aset(class_tag_map, cASN1GeneralString, INT2NUM(V_ASN1_GENERALSTRING)); rb_hash_aset(class_tag_map, cASN1UniversalString, INT2NUM(V_ASN1_UNIVERSALSTRING)); rb_hash_aset(class_tag_map, cASN1BMPString, INT2NUM(V_ASN1_BMPSTRING)); rb_global_variable(&class_tag_map); id_each = rb_intern_const("each"); } openssl-2.0.9/ext/openssl/ossl_asn1.h000066400000000000000000000040111336157045000175430ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' team members * Copyright (C) 2003 * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_ASN1_H_) #define _OSSL_ASN1_H_ /* * ASN1_DATE conversions */ VALUE asn1time_to_time(const ASN1_TIME *); #if defined(HAVE_ASN1_TIME_ADJ) /* Splits VALUE to seconds and offset days. VALUE is typically a Time or an * Integer. This is used when updating ASN1_*TIME with ASN1_TIME_adj() or * X509_time_adj_ex(). We can't use ASN1_TIME_set() and X509_time_adj() because * they have the Year 2038 issue on sizeof(time_t) == 4 environment */ void ossl_time_split(VALUE, time_t *, int *); #else time_t time_to_time_t(VALUE); #endif /* * ASN1_STRING conversions */ VALUE asn1str_to_str(const ASN1_STRING *); /* * ASN1_INTEGER conversions */ VALUE asn1integer_to_num(const ASN1_INTEGER *); ASN1_INTEGER *num_to_asn1integer(VALUE, ASN1_INTEGER *); /* * ASN1 module */ extern VALUE mASN1; extern VALUE eASN1Error; extern VALUE cASN1Data; extern VALUE cASN1Primitive; extern VALUE cASN1Constructive; extern VALUE cASN1Boolean; /* BOOLEAN */ extern VALUE cASN1Integer, cASN1Enumerated; /* INTEGER */ extern VALUE cASN1BitString; /* BIT STRING */ extern VALUE cASN1OctetString, cASN1UTF8String; /* STRINGs */ extern VALUE cASN1NumericString, cASN1PrintableString; extern VALUE cASN1T61String, cASN1VideotexString; extern VALUE cASN1IA5String, cASN1GraphicString; extern VALUE cASN1ISO64String, cASN1GeneralString; extern VALUE cASN1UniversalString, cASN1BMPString; extern VALUE cASN1Null; /* NULL */ extern VALUE cASN1ObjectId; /* OBJECT IDENTIFIER */ extern VALUE cASN1UTCTime, cASN1GeneralizedTime; /* TIME */ extern VALUE cASN1Sequence, cASN1Set; /* CONSTRUCTIVE */ ASN1_TYPE *ossl_asn1_get_asn1type(VALUE); void Init_ossl_asn1(void); #endif openssl-2.0.9/ext/openssl/ossl_bio.c000066400000000000000000000020301336157045000174440ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' team members * Copyright (C) 2003 * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" BIO * ossl_obj2bio(volatile VALUE *pobj) { VALUE obj = *pobj; BIO *bio; if (RB_TYPE_P(obj, T_FILE)) obj = rb_funcallv(obj, rb_intern("read"), 0, NULL); StringValue(obj); bio = BIO_new_mem_buf(RSTRING_PTR(obj), RSTRING_LENINT(obj)); if (!bio) ossl_raise(eOSSLError, "BIO_new_mem_buf"); *pobj = obj; return bio; } VALUE ossl_membio2str0(BIO *bio) { VALUE ret; BUF_MEM *buf; BIO_get_mem_ptr(bio, &buf); ret = rb_str_new(buf->data, buf->length); return ret; } VALUE ossl_protect_membio2str(BIO *bio, int *status) { return rb_protect((VALUE (*)(VALUE))ossl_membio2str0, (VALUE)bio, status); } VALUE ossl_membio2str(BIO *bio) { VALUE ret; int status = 0; ret = ossl_protect_membio2str(bio, &status); BIO_free(bio); if(status) rb_jump_tag(status); return ret; } openssl-2.0.9/ext/openssl/ossl_bio.h000066400000000000000000000005731336157045000174630ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' team members * Copyright (C) 2003 * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_BIO_H_) #define _OSSL_BIO_H_ BIO *ossl_obj2bio(volatile VALUE *); VALUE ossl_membio2str0(BIO*); VALUE ossl_membio2str(BIO*); VALUE ossl_protect_membio2str(BIO*,int*); #endif openssl-2.0.9/ext/openssl/ossl_bn.c000066400000000000000000000602771336157045000173130ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Technorama team * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ /* modified by Michal Rokos */ #include "ossl.h" #define NewBN(klass) \ TypedData_Wrap_Struct((klass), &ossl_bn_type, 0) #define SetBN(obj, bn) do { \ if (!(bn)) { \ ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (bn); \ } while (0) #define GetBN(obj, bn) do { \ TypedData_Get_Struct((obj), BIGNUM, &ossl_bn_type, (bn)); \ if (!(bn)) { \ ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \ } \ } while (0) #define SafeGetBN(obj, bn) do { \ OSSL_Check_Kind((obj), cBN); \ GetBN((obj), (bn)); \ } while (0) static void ossl_bn_free(void *ptr) { BN_clear_free(ptr); } static const rb_data_type_t ossl_bn_type = { "OpenSSL/BN", { 0, ossl_bn_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * Classes */ VALUE cBN; /* Document-class: OpenSSL::BNError * * Generic Error for all of OpenSSL::BN (big num) */ VALUE eBNError; /* * Public */ VALUE ossl_bn_new(const BIGNUM *bn) { BIGNUM *newbn; VALUE obj; obj = NewBN(cBN); newbn = bn ? BN_dup(bn) : BN_new(); if (!newbn) { ossl_raise(eBNError, NULL); } SetBN(obj, newbn); return obj; } static BIGNUM * integer_to_bnptr(VALUE obj, BIGNUM *orig) { BIGNUM *bn; if (FIXNUM_P(obj)) { long i; unsigned char bin[sizeof(long)]; long n = FIX2LONG(obj); unsigned long un = labs(n); for (i = sizeof(long) - 1; 0 <= i; i--) { bin[i] = un & 0xff; un >>= 8; } bn = BN_bin2bn(bin, sizeof(bin), orig); if (!bn) ossl_raise(eBNError, "BN_bin2bn"); if (n < 0) BN_set_negative(bn, 1); } else { /* assuming Bignum */ size_t len = rb_absint_size(obj, NULL); unsigned char *bin; VALUE buf; int sign; if (INT_MAX < len) { rb_raise(eBNError, "bignum too long"); } bin = (unsigned char*)ALLOCV_N(unsigned char, buf, len); sign = rb_integer_pack(obj, bin, len, 1, 0, INTEGER_PACK_BIG_ENDIAN); bn = BN_bin2bn(bin, (int)len, orig); ALLOCV_END(buf); if (!bn) ossl_raise(eBNError, "BN_bin2bn"); if (sign < 0) BN_set_negative(bn, 1); } return bn; } static VALUE try_convert_to_bn(VALUE obj) { BIGNUM *bn; VALUE newobj = Qnil; if (rb_obj_is_kind_of(obj, cBN)) return obj; if (RB_INTEGER_TYPE_P(obj)) { newobj = NewBN(cBN); /* Handle potential mem leaks */ bn = integer_to_bnptr(obj, NULL); SetBN(newobj, bn); } return newobj; } BIGNUM * ossl_bn_value_ptr(volatile VALUE *ptr) { VALUE tmp; BIGNUM *bn; tmp = try_convert_to_bn(*ptr); if (NIL_P(tmp)) ossl_raise(rb_eTypeError, "Cannot convert into OpenSSL::BN"); GetBN(tmp, bn); *ptr = tmp; return bn; } /* * Private */ /* * BN_CTX - is used in more difficult math. ops * (Why just 1? Because Ruby itself isn't thread safe, * we don't need to care about threads) */ BN_CTX *ossl_bn_ctx; static VALUE ossl_bn_alloc(VALUE klass) { BIGNUM *bn; VALUE obj = NewBN(klass); if (!(bn = BN_new())) { ossl_raise(eBNError, NULL); } SetBN(obj, bn); return obj; } /* Document-method: OpenSSL::BN.new * * call-seq: * OpenSSL::BN.new => aBN * OpenSSL::BN.new(bn) => aBN * OpenSSL::BN.new(integer) => aBN * OpenSSL::BN.new(string) => aBN * OpenSSL::BN.new(string, 0 | 2 | 10 | 16) => aBN * * Construct a new OpenSSL BigNum object. */ static VALUE ossl_bn_initialize(int argc, VALUE *argv, VALUE self) { BIGNUM *bn; VALUE str, bs; int base = 10; if (rb_scan_args(argc, argv, "11", &str, &bs) == 2) { base = NUM2INT(bs); } if (RB_INTEGER_TYPE_P(str)) { GetBN(self, bn); integer_to_bnptr(str, bn); return self; } if (RTEST(rb_obj_is_kind_of(str, cBN))) { BIGNUM *other; GetBN(self, bn); GetBN(str, other); /* Safe - we checked kind_of? above */ if (!BN_copy(bn, other)) { ossl_raise(eBNError, NULL); } return self; } GetBN(self, bn); switch (base) { case 0: if (!BN_mpi2bn((unsigned char *)StringValuePtr(str), RSTRING_LENINT(str), bn)) { ossl_raise(eBNError, NULL); } break; case 2: if (!BN_bin2bn((unsigned char *)StringValuePtr(str), RSTRING_LENINT(str), bn)) { ossl_raise(eBNError, NULL); } break; case 10: if (!BN_dec2bn(&bn, StringValueCStr(str))) { ossl_raise(eBNError, NULL); } break; case 16: if (!BN_hex2bn(&bn, StringValueCStr(str))) { ossl_raise(eBNError, NULL); } break; default: ossl_raise(rb_eArgError, "invalid radix %d", base); } return self; } /* * call-seq: * bn.to_s => string * bn.to_s(base) => string * * === Parameters * * +base+ - integer * Valid values: * * 0 - MPI * * 2 - binary * * 10 - the default * * 16 - hex */ static VALUE ossl_bn_to_s(int argc, VALUE *argv, VALUE self) { BIGNUM *bn; VALUE str, bs; int base = 10, len; char *buf; if (rb_scan_args(argc, argv, "01", &bs) == 1) { base = NUM2INT(bs); } GetBN(self, bn); switch (base) { case 0: len = BN_bn2mpi(bn, NULL); str = rb_str_new(0, len); if (BN_bn2mpi(bn, (unsigned char *)RSTRING_PTR(str)) != len) ossl_raise(eBNError, NULL); break; case 2: len = BN_num_bytes(bn); str = rb_str_new(0, len); if (BN_bn2bin(bn, (unsigned char *)RSTRING_PTR(str)) != len) ossl_raise(eBNError, NULL); break; case 10: if (!(buf = BN_bn2dec(bn))) ossl_raise(eBNError, NULL); str = ossl_buf2str(buf, rb_long2int(strlen(buf))); break; case 16: if (!(buf = BN_bn2hex(bn))) ossl_raise(eBNError, NULL); str = ossl_buf2str(buf, rb_long2int(strlen(buf))); break; default: ossl_raise(rb_eArgError, "invalid radix %d", base); } return str; } /* * call-seq: * bn.to_i => integer */ static VALUE ossl_bn_to_i(VALUE self) { BIGNUM *bn; char *txt; VALUE num; GetBN(self, bn); if (!(txt = BN_bn2hex(bn))) { ossl_raise(eBNError, NULL); } num = rb_cstr_to_inum(txt, 16, Qtrue); OPENSSL_free(txt); return num; } static VALUE ossl_bn_to_bn(VALUE self) { return self; } static VALUE ossl_bn_coerce(VALUE self, VALUE other) { switch(TYPE(other)) { case T_STRING: self = ossl_bn_to_s(0, NULL, self); break; case T_FIXNUM: case T_BIGNUM: self = ossl_bn_to_i(self); break; default: if (!RTEST(rb_obj_is_kind_of(other, cBN))) { ossl_raise(rb_eTypeError, "Don't know how to coerce"); } } return rb_assoc_new(other, self); } #define BIGNUM_BOOL1(func) \ static VALUE \ ossl_bn_##func(VALUE self) \ { \ BIGNUM *bn; \ GetBN(self, bn); \ if (BN_##func(bn)) { \ return Qtrue; \ } \ return Qfalse; \ } /* * Document-method: OpenSSL::BN#zero? * call-seq: * bn.zero? => true | false */ BIGNUM_BOOL1(is_zero) /* * Document-method: OpenSSL::BN#one? * call-seq: * bn.one? => true | false */ BIGNUM_BOOL1(is_one) /* * Document-method: OpenSSL::BN#odd? * call-seq: * bn.odd? => true | false */ BIGNUM_BOOL1(is_odd) #define BIGNUM_1c(func) \ static VALUE \ ossl_bn_##func(VALUE self) \ { \ BIGNUM *bn, *result; \ VALUE obj; \ GetBN(self, bn); \ obj = NewBN(rb_obj_class(self)); \ if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ if (!BN_##func(result, bn, ossl_bn_ctx)) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ SetBN(obj, result); \ return obj; \ } /* * Document-method: OpenSSL::BN#sqr * call-seq: * bn.sqr => aBN */ BIGNUM_1c(sqr) #define BIGNUM_2(func) \ static VALUE \ ossl_bn_##func(VALUE self, VALUE other) \ { \ BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ VALUE obj; \ GetBN(self, bn1); \ obj = NewBN(rb_obj_class(self)); \ if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ if (!BN_##func(result, bn1, bn2)) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ SetBN(obj, result); \ return obj; \ } /* * Document-method: OpenSSL::BN#+ * call-seq: * bn + bn2 => aBN */ BIGNUM_2(add) /* * Document-method: OpenSSL::BN#- * call-seq: * bn - bn2 => aBN */ BIGNUM_2(sub) #define BIGNUM_2c(func) \ static VALUE \ ossl_bn_##func(VALUE self, VALUE other) \ { \ BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \ VALUE obj; \ GetBN(self, bn1); \ obj = NewBN(rb_obj_class(self)); \ if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ if (!BN_##func(result, bn1, bn2, ossl_bn_ctx)) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ SetBN(obj, result); \ return obj; \ } /* * Document-method: OpenSSL::BN#* * call-seq: * bn * bn2 => aBN */ BIGNUM_2c(mul) /* * Document-method: OpenSSL::BN#% * call-seq: * bn % bn2 => aBN */ BIGNUM_2c(mod) /* * Document-method: OpenSSL::BN#** * call-seq: * bn ** bn2 => aBN */ BIGNUM_2c(exp) /* * Document-method: OpenSSL::BN#gcd * call-seq: * bn.gcd(bn2) => aBN */ BIGNUM_2c(gcd) /* * Document-method: OpenSSL::BN#mod_sqr * call-seq: * bn.mod_sqr(bn2) => aBN */ BIGNUM_2c(mod_sqr) /* * Document-method: OpenSSL::BN#mod_inverse * call-seq: * bn.mod_inverse(bn2) => aBN */ BIGNUM_2c(mod_inverse) /* * Document-method: OpenSSL::BN#/ * call-seq: * bn1 / bn2 => [result, remainder] * * Division of OpenSSL::BN instances */ static VALUE ossl_bn_div(VALUE self, VALUE other) { BIGNUM *bn1, *bn2 = GetBNPtr(other), *r1, *r2; VALUE klass, obj1, obj2; GetBN(self, bn1); klass = rb_obj_class(self); obj1 = NewBN(klass); obj2 = NewBN(klass); if (!(r1 = BN_new())) { ossl_raise(eBNError, NULL); } if (!(r2 = BN_new())) { BN_free(r1); ossl_raise(eBNError, NULL); } if (!BN_div(r1, r2, bn1, bn2, ossl_bn_ctx)) { BN_free(r1); BN_free(r2); ossl_raise(eBNError, NULL); } SetBN(obj1, r1); SetBN(obj2, r2); return rb_ary_new3(2, obj1, obj2); } #define BIGNUM_3c(func) \ static VALUE \ ossl_bn_##func(VALUE self, VALUE other1, VALUE other2) \ { \ BIGNUM *bn1, *bn2 = GetBNPtr(other1); \ BIGNUM *bn3 = GetBNPtr(other2), *result; \ VALUE obj; \ GetBN(self, bn1); \ obj = NewBN(rb_obj_class(self)); \ if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ if (!BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx)) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ SetBN(obj, result); \ return obj; \ } /* * Document-method: OpenSSL::BN#mod_add * call-seq: * bn.mod_add(bn1, bn2) -> aBN */ BIGNUM_3c(mod_add) /* * Document-method: OpenSSL::BN#mod_sub * call-seq: * bn.mod_sub(bn1, bn2) -> aBN */ BIGNUM_3c(mod_sub) /* * Document-method: OpenSSL::BN#mod_mul * call-seq: * bn.mod_mul(bn1, bn2) -> aBN */ BIGNUM_3c(mod_mul) /* * Document-method: OpenSSL::BN#mod_exp * call-seq: * bn.mod_exp(bn1, bn2) -> aBN */ BIGNUM_3c(mod_exp) #define BIGNUM_BIT(func) \ static VALUE \ ossl_bn_##func(VALUE self, VALUE bit) \ { \ BIGNUM *bn; \ GetBN(self, bn); \ if (!BN_##func(bn, NUM2INT(bit))) { \ ossl_raise(eBNError, NULL); \ } \ return self; \ } /* * Document-method: OpenSSL::BN#set_bit! * call-seq: * bn.set_bit!(bit) -> self */ BIGNUM_BIT(set_bit) /* * Document-method: OpenSSL::BN#clear_bit! * call-seq: * bn.clear_bit!(bit) -> self */ BIGNUM_BIT(clear_bit) /* * Document-method: OpenSSL::BN#mask_bit! * call-seq: * bn.mask_bit!(bit) -> self */ BIGNUM_BIT(mask_bits) /* Document-method: OpenSSL::BN#bit_set? * call-seq: * bn.bit_set?(bit) => true | false * * Returns boolean of whether +bit+ is set. * Bitwise operations for openssl BIGNUMs. */ static VALUE ossl_bn_is_bit_set(VALUE self, VALUE bit) { int b; BIGNUM *bn; b = NUM2INT(bit); GetBN(self, bn); if (BN_is_bit_set(bn, b)) { return Qtrue; } return Qfalse; } #define BIGNUM_SHIFT(func) \ static VALUE \ ossl_bn_##func(VALUE self, VALUE bits) \ { \ BIGNUM *bn, *result; \ int b; \ VALUE obj; \ b = NUM2INT(bits); \ GetBN(self, bn); \ obj = NewBN(rb_obj_class(self)); \ if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ if (!BN_##func(result, bn, b)) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ SetBN(obj, result); \ return obj; \ } /* * Document-method: OpenSSL::BN#<< * call-seq: * bn << bits -> aBN */ BIGNUM_SHIFT(lshift) /* * Document-method: OpenSSL::BN#>> * call-seq: * bn >> bits -> aBN */ BIGNUM_SHIFT(rshift) #define BIGNUM_SELF_SHIFT(func) \ static VALUE \ ossl_bn_self_##func(VALUE self, VALUE bits) \ { \ BIGNUM *bn; \ int b; \ b = NUM2INT(bits); \ GetBN(self, bn); \ if (!BN_##func(bn, bn, b)) \ ossl_raise(eBNError, NULL); \ return self; \ } /* * Document-method: OpenSSL::BN#lshift! * call-seq: * bn.lshift!(bits) -> self */ BIGNUM_SELF_SHIFT(lshift) /* * Document-method: OpenSSL::BN#rshift! * call-seq: * bn.rshift!(bits) -> self */ BIGNUM_SELF_SHIFT(rshift) #define BIGNUM_RAND(func) \ static VALUE \ ossl_bn_s_##func(int argc, VALUE *argv, VALUE klass) \ { \ BIGNUM *result; \ int bottom = 0, top = 0, b; \ VALUE bits, fill, odd, obj; \ \ switch (rb_scan_args(argc, argv, "12", &bits, &fill, &odd)) { \ case 3: \ bottom = (odd == Qtrue) ? 1 : 0; \ /* FALLTHROUGH */ \ case 2: \ top = NUM2INT(fill); \ } \ b = NUM2INT(bits); \ obj = NewBN(klass); \ if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ if (!BN_##func(result, b, top, bottom)) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ SetBN(obj, result); \ return obj; \ } /* * Document-method: OpenSSL::BN.rand * BN.rand(bits [, fill [, odd]]) -> aBN */ BIGNUM_RAND(rand) /* * Document-method: OpenSSL::BN.pseudo_rand * BN.pseudo_rand(bits [, fill [, odd]]) -> aBN */ BIGNUM_RAND(pseudo_rand) #define BIGNUM_RAND_RANGE(func) \ static VALUE \ ossl_bn_s_##func##_range(VALUE klass, VALUE range) \ { \ BIGNUM *bn = GetBNPtr(range), *result; \ VALUE obj = NewBN(klass); \ if (!(result = BN_new())) { \ ossl_raise(eBNError, NULL); \ } \ if (!BN_##func##_range(result, bn)) { \ BN_free(result); \ ossl_raise(eBNError, NULL); \ } \ SetBN(obj, result); \ return obj; \ } /* * Document-method: OpenSSL::BN.rand_range * call-seq: * BN.rand_range(range) -> aBN * */ BIGNUM_RAND_RANGE(rand) /* * Document-method: OpenSSL::BN.pseudo_rand_range * call-seq: * BN.pseudo_rand_range(range) -> aBN * */ BIGNUM_RAND_RANGE(pseudo_rand) /* * call-seq: * BN.generate_prime(bits, [, safe [, add [, rem]]]) => bn * * Generates a random prime number of bit length +bits+. If +safe+ is true, * generates a safe prime. If +add+ is specified, generates a prime that * fulfills condition p % add = rem. * * === Parameters * * +bits+ - integer * * +safe+ - boolean * * +add+ - BN * * +rem+ - BN */ static VALUE ossl_bn_s_generate_prime(int argc, VALUE *argv, VALUE klass) { BIGNUM *add = NULL, *rem = NULL, *result; int safe = 1, num; VALUE vnum, vsafe, vadd, vrem, obj; rb_scan_args(argc, argv, "13", &vnum, &vsafe, &vadd, &vrem); num = NUM2INT(vnum); if (vsafe == Qfalse) { safe = 0; } if (!NIL_P(vadd)) { add = GetBNPtr(vadd); rem = NIL_P(vrem) ? NULL : GetBNPtr(vrem); } obj = NewBN(klass); if (!(result = BN_new())) { ossl_raise(eBNError, NULL); } if (!BN_generate_prime_ex(result, num, safe, add, rem, NULL)) { BN_free(result); ossl_raise(eBNError, NULL); } SetBN(obj, result); return obj; } #define BIGNUM_NUM(func) \ static VALUE \ ossl_bn_##func(VALUE self) \ { \ BIGNUM *bn; \ GetBN(self, bn); \ return INT2NUM(BN_##func(bn)); \ } /* * Document-method: OpenSSL::BN#num_bytes * call-seq: * bn.num_bytes => integer */ BIGNUM_NUM(num_bytes) /* * Document-method: OpenSSL::BN#num_bits * call-seq: * bn.num_bits => integer */ BIGNUM_NUM(num_bits) static VALUE ossl_bn_copy(VALUE self, VALUE other) { BIGNUM *bn1, *bn2; rb_check_frozen(self); if (self == other) return self; GetBN(self, bn1); bn2 = GetBNPtr(other); if (!BN_copy(bn1, bn2)) { ossl_raise(eBNError, NULL); } return self; } #define BIGNUM_CMP(func) \ static VALUE \ ossl_bn_##func(VALUE self, VALUE other) \ { \ BIGNUM *bn1, *bn2 = GetBNPtr(other); \ GetBN(self, bn1); \ return INT2NUM(BN_##func(bn1, bn2)); \ } /* * Document-method: OpenSSL::BN#cmp * call-seq: * bn.cmp(bn2) => integer */ /* * Document-method: OpenSSL::BN#<=> * call-seq: * bn <=> bn2 => integer */ BIGNUM_CMP(cmp) /* * Document-method: OpenSSL::BN#ucmp * call-seq: * bn.ucmp(bn2) => integer */ BIGNUM_CMP(ucmp) /* * call-seq: * bn == obj => true or false * * Returns +true+ only if +obj+ has the same value as +bn+. Contrast this * with OpenSSL::BN#eql?, which requires obj to be OpenSSL::BN. */ static VALUE ossl_bn_eq(VALUE self, VALUE other) { BIGNUM *bn1, *bn2; GetBN(self, bn1); other = try_convert_to_bn(other); if (NIL_P(other)) return Qfalse; GetBN(other, bn2); if (!BN_cmp(bn1, bn2)) { return Qtrue; } return Qfalse; } /* * call-seq: * bn.eql?(obj) => true or false * * Returns true only if obj is a * OpenSSL::BN with the same value as big. Contrast this * with OpenSSL::BN#==, which performs type conversions. */ static VALUE ossl_bn_eql(VALUE self, VALUE other) { BIGNUM *bn1, *bn2; if (!rb_obj_is_kind_of(other, cBN)) return Qfalse; GetBN(self, bn1); GetBN(other, bn2); return BN_cmp(bn1, bn2) ? Qfalse : Qtrue; } /* * call-seq: * bn.hash => Integer * * Returns a hash code for this object. * * See also Object#hash. */ static VALUE ossl_bn_hash(VALUE self) { BIGNUM *bn; VALUE hash; unsigned char *buf; int len; GetBN(self, bn); len = BN_num_bytes(bn); buf = xmalloc(len); if (BN_bn2bin(bn, buf) != len) { xfree(buf); ossl_raise(eBNError, NULL); } hash = ST2FIX(rb_memhash(buf, len)); xfree(buf); return hash; } /* * call-seq: * bn.prime? => true | false * bn.prime?(checks) => true | false * * Performs a Miller-Rabin probabilistic primality test with +checks+ * iterations. If +nchecks+ is not specified, a number of iterations is used * that yields a false positive rate of at most 2^-80 for random input. * * === Parameters * * +checks+ - integer */ static VALUE ossl_bn_is_prime(int argc, VALUE *argv, VALUE self) { BIGNUM *bn; VALUE vchecks; int checks = BN_prime_checks; if (rb_scan_args(argc, argv, "01", &vchecks) == 1) { checks = NUM2INT(vchecks); } GetBN(self, bn); switch (BN_is_prime_ex(bn, checks, ossl_bn_ctx, NULL)) { case 1: return Qtrue; case 0: return Qfalse; default: ossl_raise(eBNError, NULL); } /* not reachable */ return Qnil; } /* * call-seq: * bn.prime_fasttest? => true | false * bn.prime_fasttest?(checks) => true | false * bn.prime_fasttest?(checks, trial_div) => true | false * * Performs a Miller-Rabin primality test. This is same as #prime? except this * first attempts trial divisions with some small primes. * * === Parameters * * +checks+ - integer * * +trial_div+ - boolean */ static VALUE ossl_bn_is_prime_fasttest(int argc, VALUE *argv, VALUE self) { BIGNUM *bn; VALUE vchecks, vtrivdiv; int checks = BN_prime_checks, do_trial_division = 1; rb_scan_args(argc, argv, "02", &vchecks, &vtrivdiv); if (!NIL_P(vchecks)) { checks = NUM2INT(vchecks); } GetBN(self, bn); /* handle true/false */ if (vtrivdiv == Qfalse) { do_trial_division = 0; } switch (BN_is_prime_fasttest_ex(bn, checks, ossl_bn_ctx, do_trial_division, NULL)) { case 1: return Qtrue; case 0: return Qfalse; default: ossl_raise(eBNError, NULL); } /* not reachable */ return Qnil; } /* * INIT * (NOTE: ordering of methods is the same as in 'man bn') */ void Init_ossl_bn(void) { #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif if (!(ossl_bn_ctx = BN_CTX_new())) { ossl_raise(rb_eRuntimeError, "Cannot init BN_CTX"); } eBNError = rb_define_class_under(mOSSL, "BNError", eOSSLError); cBN = rb_define_class_under(mOSSL, "BN", rb_cObject); rb_define_alloc_func(cBN, ossl_bn_alloc); rb_define_method(cBN, "initialize", ossl_bn_initialize, -1); rb_define_copy_func(cBN, ossl_bn_copy); rb_define_method(cBN, "copy", ossl_bn_copy, 1); /* swap (=coerce?) */ rb_define_method(cBN, "num_bytes", ossl_bn_num_bytes, 0); rb_define_method(cBN, "num_bits", ossl_bn_num_bits, 0); /* num_bits_word */ rb_define_method(cBN, "+", ossl_bn_add, 1); rb_define_method(cBN, "-", ossl_bn_sub, 1); rb_define_method(cBN, "*", ossl_bn_mul, 1); rb_define_method(cBN, "sqr", ossl_bn_sqr, 0); rb_define_method(cBN, "/", ossl_bn_div, 1); rb_define_method(cBN, "%", ossl_bn_mod, 1); /* nnmod */ rb_define_method(cBN, "mod_add", ossl_bn_mod_add, 2); rb_define_method(cBN, "mod_sub", ossl_bn_mod_sub, 2); rb_define_method(cBN, "mod_mul", ossl_bn_mod_mul, 2); rb_define_method(cBN, "mod_sqr", ossl_bn_mod_sqr, 1); rb_define_method(cBN, "**", ossl_bn_exp, 1); rb_define_method(cBN, "mod_exp", ossl_bn_mod_exp, 2); rb_define_method(cBN, "gcd", ossl_bn_gcd, 1); /* add_word * sub_word * mul_word * div_word * mod_word */ rb_define_method(cBN, "cmp", ossl_bn_cmp, 1); rb_define_alias(cBN, "<=>", "cmp"); rb_define_method(cBN, "ucmp", ossl_bn_ucmp, 1); rb_define_method(cBN, "eql?", ossl_bn_eql, 1); rb_define_method(cBN, "hash", ossl_bn_hash, 0); rb_define_method(cBN, "==", ossl_bn_eq, 1); rb_define_alias(cBN, "===", "=="); rb_define_method(cBN, "zero?", ossl_bn_is_zero, 0); rb_define_method(cBN, "one?", ossl_bn_is_one, 0); /* is_word */ rb_define_method(cBN, "odd?", ossl_bn_is_odd, 0); /* zero * one * value_one - DON'T IMPL. * set_word * get_word */ rb_define_singleton_method(cBN, "rand", ossl_bn_s_rand, -1); rb_define_singleton_method(cBN, "pseudo_rand", ossl_bn_s_pseudo_rand, -1); rb_define_singleton_method(cBN, "rand_range", ossl_bn_s_rand_range, 1); rb_define_singleton_method(cBN, "pseudo_rand_range", ossl_bn_s_pseudo_rand_range, 1); rb_define_singleton_method(cBN, "generate_prime", ossl_bn_s_generate_prime, -1); rb_define_method(cBN, "prime?", ossl_bn_is_prime, -1); rb_define_method(cBN, "prime_fasttest?", ossl_bn_is_prime_fasttest, -1); rb_define_method(cBN, "set_bit!", ossl_bn_set_bit, 1); rb_define_method(cBN, "clear_bit!", ossl_bn_clear_bit, 1); rb_define_method(cBN, "bit_set?", ossl_bn_is_bit_set, 1); rb_define_method(cBN, "mask_bits!", ossl_bn_mask_bits, 1); rb_define_method(cBN, "<<", ossl_bn_lshift, 1); rb_define_method(cBN, ">>", ossl_bn_rshift, 1); rb_define_method(cBN, "lshift!", ossl_bn_self_lshift, 1); rb_define_method(cBN, "rshift!", ossl_bn_self_rshift, 1); /* lshift1 - DON'T IMPL. */ /* rshift1 - DON'T IMPL. */ /* * bn2bin * bin2bn * bn2hex * bn2dec * hex2bn * dec2bn - all these are implemented in ossl_bn_initialize, and ossl_bn_to_s * print - NOT IMPL. * print_fp - NOT IMPL. * bn2mpi * mpi2bn */ rb_define_method(cBN, "to_s", ossl_bn_to_s, -1); rb_define_method(cBN, "to_i", ossl_bn_to_i, 0); rb_define_alias(cBN, "to_int", "to_i"); rb_define_method(cBN, "to_bn", ossl_bn_to_bn, 0); rb_define_method(cBN, "coerce", ossl_bn_coerce, 1); /* * TODO: * But how to: from_bin, from_mpi? PACK? * to_bin * to_mpi */ rb_define_method(cBN, "mod_inverse", ossl_bn_mod_inverse, 1); /* RECiProcal * MONTgomery */ } openssl-2.0.9/ext/openssl/ossl_bn.h000066400000000000000000000010051336157045000173000ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_BN_H_) #define _OSSL_BN_H_ extern VALUE cBN; extern VALUE eBNError; extern BN_CTX *ossl_bn_ctx; #define GetBNPtr(obj) ossl_bn_value_ptr(&(obj)) VALUE ossl_bn_new(const BIGNUM *); BIGNUM *ossl_bn_value_ptr(volatile VALUE *); void Init_ossl_bn(void); #endif /* _OSS_BN_H_ */ openssl-2.0.9/ext/openssl/ossl_cipher.c000066400000000000000000001033101336157045000201500ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define NewCipher(klass) \ TypedData_Wrap_Struct((klass), &ossl_cipher_type, 0) #define AllocCipher(obj, ctx) do { \ (ctx) = EVP_CIPHER_CTX_new(); \ if (!(ctx)) \ ossl_raise(rb_eRuntimeError, NULL); \ RTYPEDDATA_DATA(obj) = (ctx); \ } while (0) #define GetCipherInit(obj, ctx) do { \ TypedData_Get_Struct((obj), EVP_CIPHER_CTX, &ossl_cipher_type, (ctx)); \ } while (0) #define GetCipher(obj, ctx) do { \ GetCipherInit((obj), (ctx)); \ if (!(ctx)) { \ ossl_raise(rb_eRuntimeError, "Cipher not initialized!"); \ } \ } while (0) #define SafeGetCipher(obj, ctx) do { \ OSSL_Check_Kind((obj), cCipher); \ GetCipher((obj), (ctx)); \ } while (0) /* * Classes */ VALUE cCipher; VALUE eCipherError; static ID id_auth_tag_len, id_key_set; static VALUE ossl_cipher_alloc(VALUE klass); static void ossl_cipher_free(void *ptr); static const rb_data_type_t ossl_cipher_type = { "OpenSSL/Cipher", { 0, ossl_cipher_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * PUBLIC */ const EVP_CIPHER * GetCipherPtr(VALUE obj) { if (rb_obj_is_kind_of(obj, cCipher)) { EVP_CIPHER_CTX *ctx; GetCipher(obj, ctx); return EVP_CIPHER_CTX_cipher(ctx); } else { const EVP_CIPHER *cipher; StringValueCStr(obj); cipher = EVP_get_cipherbyname(RSTRING_PTR(obj)); if (!cipher) ossl_raise(rb_eArgError, "unsupported cipher algorithm: %"PRIsVALUE, obj); return cipher; } } VALUE ossl_cipher_new(const EVP_CIPHER *cipher) { VALUE ret; EVP_CIPHER_CTX *ctx; ret = ossl_cipher_alloc(cCipher); AllocCipher(ret, ctx); if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1) ossl_raise(eCipherError, NULL); return ret; } /* * PRIVATE */ static void ossl_cipher_free(void *ptr) { EVP_CIPHER_CTX_free(ptr); } static VALUE ossl_cipher_alloc(VALUE klass) { return NewCipher(klass); } /* * call-seq: * Cipher.new(string) -> cipher * * The string must contain a valid cipher name like "AES-128-CBC" or "3DES". * * A list of cipher names is available by calling OpenSSL::Cipher.ciphers. */ static VALUE ossl_cipher_initialize(VALUE self, VALUE str) { EVP_CIPHER_CTX *ctx; const EVP_CIPHER *cipher; char *name; name = StringValueCStr(str); GetCipherInit(self, ctx); if (ctx) { ossl_raise(rb_eRuntimeError, "Cipher already initialized!"); } AllocCipher(self, ctx); if (!(cipher = EVP_get_cipherbyname(name))) { ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%"PRIsVALUE")", str); } if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1) ossl_raise(eCipherError, NULL); return self; } static VALUE ossl_cipher_copy(VALUE self, VALUE other) { EVP_CIPHER_CTX *ctx1, *ctx2; rb_check_frozen(self); if (self == other) return self; GetCipherInit(self, ctx1); if (!ctx1) { AllocCipher(self, ctx1); } SafeGetCipher(other, ctx2); if (EVP_CIPHER_CTX_copy(ctx1, ctx2) != 1) ossl_raise(eCipherError, NULL); return self; } static void* add_cipher_name_to_ary(const OBJ_NAME *name, VALUE ary) { rb_ary_push(ary, rb_str_new2(name->name)); return NULL; } /* * call-seq: * OpenSSL::Cipher.ciphers -> array[string...] * * Returns the names of all available ciphers in an array. */ static VALUE ossl_s_ciphers(VALUE self) { VALUE ary; ary = rb_ary_new(); OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH, (void(*)(const OBJ_NAME*,void*))add_cipher_name_to_ary, (void*)ary); return ary; } /* * call-seq: * cipher.reset -> self * * Fully resets the internal state of the Cipher. By using this, the same * Cipher instance may be used several times for encryption or decryption tasks. * * Internally calls EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, -1). */ static VALUE ossl_cipher_reset(VALUE self) { EVP_CIPHER_CTX *ctx; GetCipher(self, ctx); if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, -1) != 1) ossl_raise(eCipherError, NULL); return self; } static VALUE ossl_cipher_init(int argc, VALUE *argv, VALUE self, int mode) { EVP_CIPHER_CTX *ctx; unsigned char key[EVP_MAX_KEY_LENGTH], *p_key = NULL; unsigned char iv[EVP_MAX_IV_LENGTH], *p_iv = NULL; VALUE pass, init_v; if(rb_scan_args(argc, argv, "02", &pass, &init_v) > 0){ /* * oops. this code mistakes salt for IV. * We deprecated the arguments for this method, but we decided * keeping this behaviour for backward compatibility. */ VALUE cname = rb_class_path(rb_obj_class(self)); rb_warn("arguments for %"PRIsVALUE"#encrypt and %"PRIsVALUE"#decrypt were deprecated; " "use %"PRIsVALUE"#pkcs5_keyivgen to derive key and IV", cname, cname, cname); StringValue(pass); GetCipher(self, ctx); if (NIL_P(init_v)) memcpy(iv, "OpenSSL for Ruby rulez!", sizeof(iv)); else{ StringValue(init_v); if (EVP_MAX_IV_LENGTH > RSTRING_LEN(init_v)) { memset(iv, 0, EVP_MAX_IV_LENGTH); memcpy(iv, RSTRING_PTR(init_v), RSTRING_LEN(init_v)); } else memcpy(iv, RSTRING_PTR(init_v), sizeof(iv)); } EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), EVP_md5(), iv, (unsigned char *)RSTRING_PTR(pass), RSTRING_LENINT(pass), 1, key, NULL); p_key = key; p_iv = iv; } else { GetCipher(self, ctx); } if (EVP_CipherInit_ex(ctx, NULL, NULL, p_key, p_iv, mode) != 1) { ossl_raise(eCipherError, NULL); } if (p_key) rb_ivar_set(self, id_key_set, Qtrue); return self; } /* * call-seq: * cipher.encrypt -> self * * Initializes the Cipher for encryption. * * Make sure to call Cipher#encrypt or Cipher#decrypt before using any of the * following methods: * * [#key=, #iv=, #random_key, #random_iv, #pkcs5_keyivgen] * * Internally calls EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, 1). */ static VALUE ossl_cipher_encrypt(int argc, VALUE *argv, VALUE self) { return ossl_cipher_init(argc, argv, self, 1); } /* * call-seq: * cipher.decrypt -> self * * Initializes the Cipher for decryption. * * Make sure to call Cipher#encrypt or Cipher#decrypt before using any of the * following methods: * * [#key=, #iv=, #random_key, #random_iv, #pkcs5_keyivgen] * * Internally calls EVP_CipherInit_ex(ctx, NULL, NULL, NULL, NULL, 0). */ static VALUE ossl_cipher_decrypt(int argc, VALUE *argv, VALUE self) { return ossl_cipher_init(argc, argv, self, 0); } /* * call-seq: * cipher.pkcs5_keyivgen(pass, salt = nil, iterations = 2048, digest = "MD5") -> nil * * Generates and sets the key/IV based on a password. * * *WARNING*: This method is only PKCS5 v1.5 compliant when using RC2, RC4-40, * or DES with MD5 or SHA1. Using anything else (like AES) will generate the * key/iv using an OpenSSL specific method. This method is deprecated and * should no longer be used. Use a PKCS5 v2 key generation method from * OpenSSL::PKCS5 instead. * * === Parameters * * +salt+ must be an 8 byte string if provided. * * +iterations+ is an integer with a default of 2048. * * +digest+ is a Digest object that defaults to 'MD5' * * A minimum of 1000 iterations is recommended. * */ static VALUE ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self) { EVP_CIPHER_CTX *ctx; const EVP_MD *digest; VALUE vpass, vsalt, viter, vdigest; unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH], *salt = NULL; int iter; rb_scan_args(argc, argv, "13", &vpass, &vsalt, &viter, &vdigest); StringValue(vpass); if(!NIL_P(vsalt)){ StringValue(vsalt); if(RSTRING_LEN(vsalt) != PKCS5_SALT_LEN) ossl_raise(eCipherError, "salt must be an 8-octet string"); salt = (unsigned char *)RSTRING_PTR(vsalt); } iter = NIL_P(viter) ? 2048 : NUM2INT(viter); if (iter <= 0) rb_raise(rb_eArgError, "iterations must be a positive integer"); digest = NIL_P(vdigest) ? EVP_md5() : GetDigestPtr(vdigest); GetCipher(self, ctx); EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), digest, salt, (unsigned char *)RSTRING_PTR(vpass), RSTRING_LENINT(vpass), iter, key, iv); if (EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, -1) != 1) ossl_raise(eCipherError, NULL); OPENSSL_cleanse(key, sizeof key); OPENSSL_cleanse(iv, sizeof iv); rb_ivar_set(self, id_key_set, Qtrue); return Qnil; } static int ossl_cipher_update_long(EVP_CIPHER_CTX *ctx, unsigned char *out, long *out_len_ptr, const unsigned char *in, long in_len) { int out_part_len; int limit = INT_MAX / 2 + 1; long out_len = 0; do { int in_part_len = in_len > limit ? limit : (int)in_len; if (!EVP_CipherUpdate(ctx, out ? (out + out_len) : 0, &out_part_len, in, in_part_len)) return 0; out_len += out_part_len; in += in_part_len; } while ((in_len -= limit) > 0); if (out_len_ptr) *out_len_ptr = out_len; return 1; } /* * call-seq: * cipher.update(data [, buffer]) -> string or buffer * * Encrypts data in a streaming fashion. Hand consecutive blocks of data * to the +update+ method in order to encrypt it. Returns the encrypted * data chunk. When done, the output of Cipher#final should be additionally * added to the result. * * If +buffer+ is given, the encryption/decryption result will be written to * it. +buffer+ will be resized automatically. */ static VALUE ossl_cipher_update(int argc, VALUE *argv, VALUE self) { EVP_CIPHER_CTX *ctx; unsigned char *in; long in_len, out_len; VALUE data, str; rb_scan_args(argc, argv, "11", &data, &str); if (!RTEST(rb_attr_get(self, id_key_set))) ossl_raise(eCipherError, "key not set"); StringValue(data); in = (unsigned char *)RSTRING_PTR(data); if ((in_len = RSTRING_LEN(data)) == 0) ossl_raise(rb_eArgError, "data must not be empty"); GetCipher(self, ctx); out_len = in_len+EVP_CIPHER_CTX_block_size(ctx); if (out_len <= 0) { ossl_raise(rb_eRangeError, "data too big to make output buffer: %ld bytes", in_len); } if (NIL_P(str)) { str = rb_str_new(0, out_len); } else { StringValue(str); rb_str_resize(str, out_len); } if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str), &out_len, in, in_len)) ossl_raise(eCipherError, NULL); assert(out_len < RSTRING_LEN(str)); rb_str_set_len(str, out_len); return str; } /* * call-seq: * cipher.final -> string * * Returns the remaining data held in the cipher object. Further calls to * Cipher#update or Cipher#final will return garbage. This call should always * be made as the last call of an encryption or decryption operation, after * having fed the entire plaintext or ciphertext to the Cipher instance. * * If an authenticated cipher was used, a CipherError is raised if the tag * could not be authenticated successfully. Only call this method after * setting the authentication tag and passing the entire contents of the * ciphertext into the cipher. */ static VALUE ossl_cipher_final(VALUE self) { EVP_CIPHER_CTX *ctx; int out_len; VALUE str; GetCipher(self, ctx); str = rb_str_new(0, EVP_CIPHER_CTX_block_size(ctx)); if (!EVP_CipherFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), &out_len)) ossl_raise(eCipherError, NULL); assert(out_len <= RSTRING_LEN(str)); rb_str_set_len(str, out_len); return str; } /* * call-seq: * cipher.name -> string * * Returns the name of the cipher which may differ slightly from the original * name provided. */ static VALUE ossl_cipher_name(VALUE self) { EVP_CIPHER_CTX *ctx; GetCipher(self, ctx); return rb_str_new2(EVP_CIPHER_name(EVP_CIPHER_CTX_cipher(ctx))); } /* * call-seq: * cipher.key = string -> string * * Sets the cipher key. To generate a key, you should either use a secure * random byte string or, if the key is to be derived from a password, you * should rely on PBKDF2 functionality provided by OpenSSL::PKCS5. To * generate a secure random-based key, Cipher#random_key may be used. * * Only call this method after calling Cipher#encrypt or Cipher#decrypt. */ static VALUE ossl_cipher_set_key(VALUE self, VALUE key) { EVP_CIPHER_CTX *ctx; int key_len; StringValue(key); GetCipher(self, ctx); key_len = EVP_CIPHER_CTX_key_length(ctx); if (RSTRING_LEN(key) != key_len) ossl_raise(rb_eArgError, "key must be %d bytes", key_len); if (EVP_CipherInit_ex(ctx, NULL, NULL, (unsigned char *)RSTRING_PTR(key), NULL, -1) != 1) ossl_raise(eCipherError, NULL); rb_ivar_set(self, id_key_set, Qtrue); return key; } /* * call-seq: * cipher.iv = string -> string * * Sets the cipher IV. Please note that since you should never be using ECB * mode, an IV is always explicitly required and should be set prior to * encryption. The IV itself can be safely transmitted in public, but it * should be unpredictable to prevent certain kinds of attacks. You may use * Cipher#random_iv to create a secure random IV. * * Only call this method after calling Cipher#encrypt or Cipher#decrypt. */ static VALUE ossl_cipher_set_iv(VALUE self, VALUE iv) { EVP_CIPHER_CTX *ctx; int iv_len = 0; StringValue(iv); GetCipher(self, ctx); #if defined(HAVE_AUTHENTICATED_ENCRYPTION) if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) iv_len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); #endif if (!iv_len) iv_len = EVP_CIPHER_CTX_iv_length(ctx); if (RSTRING_LEN(iv) != iv_len) ossl_raise(rb_eArgError, "iv must be %d bytes", iv_len); if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, (unsigned char *)RSTRING_PTR(iv), -1) != 1) ossl_raise(eCipherError, NULL); return iv; } /* * call-seq: * cipher.authenticated? -> true | false * * Indicated whether this Cipher instance uses an Authenticated Encryption * mode. */ static VALUE ossl_cipher_is_authenticated(VALUE self) { EVP_CIPHER_CTX *ctx; GetCipher(self, ctx); #if defined(HAVE_AUTHENTICATED_ENCRYPTION) return (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse; #else return Qfalse; #endif } #ifdef HAVE_AUTHENTICATED_ENCRYPTION /* * call-seq: * cipher.auth_data = string -> string * * Sets the cipher's additional authenticated data. This field must be * set when using AEAD cipher modes such as GCM or CCM. If no associated * data shall be used, this method must *still* be called with a value of "". * The contents of this field should be non-sensitive data which will be * added to the ciphertext to generate the authentication tag which validates * the contents of the ciphertext. * * The AAD must be set prior to encryption or decryption. In encryption mode, * it must be set after calling Cipher#encrypt and setting Cipher#key= and * Cipher#iv=. When decrypting, the authenticated data must be set after key, * iv and especially *after* the authentication tag has been set. I.e. set it * only after calling Cipher#decrypt, Cipher#key=, Cipher#iv= and * Cipher#auth_tag= first. */ static VALUE ossl_cipher_set_auth_data(VALUE self, VALUE data) { EVP_CIPHER_CTX *ctx; unsigned char *in; long in_len, out_len; StringValue(data); in = (unsigned char *) RSTRING_PTR(data); in_len = RSTRING_LEN(data); GetCipher(self, ctx); if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "AEAD not supported by this cipher"); if (!ossl_cipher_update_long(ctx, NULL, &out_len, in, in_len)) ossl_raise(eCipherError, "couldn't set additional authenticated data"); return data; } /* * call-seq: * cipher.auth_tag(tag_len = 16) -> String * * Gets the authentication tag generated by Authenticated Encryption Cipher * modes (GCM for example). This tag may be stored along with the ciphertext, * then set on the decryption cipher to authenticate the contents of the * ciphertext against changes. If the optional integer parameter +tag_len+ is * given, the returned tag will be +tag_len+ bytes long. If the parameter is * omitted, the default length of 16 bytes or the length previously set by * #auth_tag_len= will be used. For maximum security, the longest possible * should be chosen. * * The tag may only be retrieved after calling Cipher#final. */ static VALUE ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self) { VALUE vtag_len, ret; EVP_CIPHER_CTX *ctx; int tag_len = 16; rb_scan_args(argc, argv, "01", &vtag_len); if (NIL_P(vtag_len)) vtag_len = rb_attr_get(self, id_auth_tag_len); if (!NIL_P(vtag_len)) tag_len = NUM2INT(vtag_len); GetCipher(self, ctx); if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "authentication tag not supported by this cipher"); ret = rb_str_new(NULL, tag_len); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, RSTRING_PTR(ret))) ossl_raise(eCipherError, "retrieving the authentication tag failed"); return ret; } /* * call-seq: * cipher.auth_tag = string -> string * * Sets the authentication tag to verify the integrity of the ciphertext. * This can be called only when the cipher supports AE. The tag must be set * after calling Cipher#decrypt, Cipher#key= and Cipher#iv=, but before * calling Cipher#final. After all decryption is performed, the tag is * verified automatically in the call to Cipher#final. * * For OCB mode, the tag length must be supplied with #auth_tag_len= * beforehand. */ static VALUE ossl_cipher_set_auth_tag(VALUE self, VALUE vtag) { EVP_CIPHER_CTX *ctx; unsigned char *tag; int tag_len; StringValue(vtag); tag = (unsigned char *) RSTRING_PTR(vtag); tag_len = RSTRING_LENINT(vtag); GetCipher(self, ctx); if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "authentication tag not supported by this cipher"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag)) ossl_raise(eCipherError, "unable to set AEAD tag"); return vtag; } /* * call-seq: * cipher.auth_tag_len = Integer -> Integer * * Sets the length of the authentication tag to be generated or to be given for * AEAD ciphers that requires it as in input parameter. Note that not all AEAD * ciphers support this method. * * In OCB mode, the length must be supplied both when encrypting and when * decrypting, and must be before specifying an IV. */ static VALUE ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen) { int tag_len = NUM2INT(vlen); EVP_CIPHER_CTX *ctx; GetCipher(self, ctx); if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "AEAD not supported by this cipher"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, NULL)) ossl_raise(eCipherError, "unable to set authentication tag length"); /* for #auth_tag */ rb_ivar_set(self, id_auth_tag_len, INT2NUM(tag_len)); return vlen; } /* * call-seq: * cipher.iv_len = integer -> integer * * Sets the IV/nonce length of the Cipher. Normally block ciphers don't allow * changing the IV length, but some make use of IV for 'nonce'. You may need * this for interoperability with other applications. */ static VALUE ossl_cipher_set_iv_length(VALUE self, VALUE iv_length) { int len = NUM2INT(iv_length); EVP_CIPHER_CTX *ctx; GetCipher(self, ctx); if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "cipher does not support AEAD"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, len, NULL)) ossl_raise(eCipherError, "unable to set IV length"); /* * EVP_CIPHER_CTX_iv_length() returns the default length. So we need to save * the length somewhere. Luckily currently we aren't using app_data. */ EVP_CIPHER_CTX_set_app_data(ctx, (void *)(VALUE)len); return iv_length; } #else #define ossl_cipher_set_auth_data rb_f_notimplement #define ossl_cipher_get_auth_tag rb_f_notimplement #define ossl_cipher_set_auth_tag rb_f_notimplement #define ossl_cipher_set_auth_tag_len rb_f_notimplement #define ossl_cipher_set_iv_length rb_f_notimplement #endif /* * call-seq: * cipher.key_len = integer -> integer * * Sets the key length of the cipher. If the cipher is a fixed length cipher * then attempting to set the key length to any value other than the fixed * value is an error. * * Under normal circumstances you do not need to call this method (and probably shouldn't). * * See EVP_CIPHER_CTX_set_key_length for further information. */ static VALUE ossl_cipher_set_key_length(VALUE self, VALUE key_length) { int len = NUM2INT(key_length); EVP_CIPHER_CTX *ctx; GetCipher(self, ctx); if (EVP_CIPHER_CTX_set_key_length(ctx, len) != 1) ossl_raise(eCipherError, NULL); return key_length; } /* * call-seq: * cipher.padding = integer -> integer * * Enables or disables padding. By default encryption operations are padded using standard block padding and the * padding is checked and removed when decrypting. If the pad parameter is zero then no padding is performed, the * total amount of data encrypted or decrypted must then be a multiple of the block size or an error will occur. * * See EVP_CIPHER_CTX_set_padding for further information. */ static VALUE ossl_cipher_set_padding(VALUE self, VALUE padding) { EVP_CIPHER_CTX *ctx; int pad = NUM2INT(padding); GetCipher(self, ctx); if (EVP_CIPHER_CTX_set_padding(ctx, pad) != 1) ossl_raise(eCipherError, NULL); return padding; } /* * call-seq: * cipher.key_len -> integer * * Returns the key length in bytes of the Cipher. */ static VALUE ossl_cipher_key_length(VALUE self) { EVP_CIPHER_CTX *ctx; GetCipher(self, ctx); return INT2NUM(EVP_CIPHER_CTX_key_length(ctx)); } /* * call-seq: * cipher.iv_len -> integer * * Returns the expected length in bytes for an IV for this Cipher. */ static VALUE ossl_cipher_iv_length(VALUE self) { EVP_CIPHER_CTX *ctx; int len = 0; GetCipher(self, ctx); #if defined(HAVE_AUTHENTICATED_ENCRYPTION) if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); #endif if (!len) len = EVP_CIPHER_CTX_iv_length(ctx); return INT2NUM(len); } /* * call-seq: * cipher.block_size -> integer * * Returns the size in bytes of the blocks on which this Cipher operates on. */ static VALUE ossl_cipher_block_size(VALUE self) { EVP_CIPHER_CTX *ctx; GetCipher(self, ctx); return INT2NUM(EVP_CIPHER_CTX_block_size(ctx)); } /* * INIT */ void Init_ossl_cipher(void) { #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif /* Document-class: OpenSSL::Cipher * * Provides symmetric algorithms for encryption and decryption. The * algorithms that are available depend on the particular version * of OpenSSL that is installed. * * === Listing all supported algorithms * * A list of supported algorithms can be obtained by * * puts OpenSSL::Cipher.ciphers * * === Instantiating a Cipher * * There are several ways to create a Cipher instance. Generally, a * Cipher algorithm is categorized by its name, the key length in bits * and the cipher mode to be used. The most generic way to create a * Cipher is the following * * cipher = OpenSSL::Cipher.new('--') * * That is, a string consisting of the hyphenated concatenation of the * individual components name, key length and mode. Either all uppercase * or all lowercase strings may be used, for example: * * cipher = OpenSSL::Cipher.new('AES-128-CBC') * * For each algorithm supported, there is a class defined under the * Cipher class that goes by the name of the cipher, e.g. to obtain an * instance of AES, you could also use * * # these are equivalent * cipher = OpenSSL::Cipher::AES.new(128, :CBC) * cipher = OpenSSL::Cipher::AES.new(128, 'CBC') * cipher = OpenSSL::Cipher::AES.new('128-CBC') * * Finally, due to its wide-spread use, there are also extra classes * defined for the different key sizes of AES * * cipher = OpenSSL::Cipher::AES128.new(:CBC) * cipher = OpenSSL::Cipher::AES192.new(:CBC) * cipher = OpenSSL::Cipher::AES256.new(:CBC) * * === Choosing either encryption or decryption mode * * Encryption and decryption are often very similar operations for * symmetric algorithms, this is reflected by not having to choose * different classes for either operation, both can be done using the * same class. Still, after obtaining a Cipher instance, we need to * tell the instance what it is that we intend to do with it, so we * need to call either * * cipher.encrypt * * or * * cipher.decrypt * * on the Cipher instance. This should be the first call after creating * the instance, otherwise configuration that has already been set could * get lost in the process. * * === Choosing a key * * Symmetric encryption requires a key that is the same for the encrypting * and for the decrypting party and after initial key establishment should * be kept as private information. There are a lot of ways to create * insecure keys, the most notable is to simply take a password as the key * without processing the password further. A simple and secure way to * create a key for a particular Cipher is * * cipher = OpenSSL::AES256.new(:CFB) * cipher.encrypt * key = cipher.random_key # also sets the generated key on the Cipher * * If you absolutely need to use passwords as encryption keys, you * should use Password-Based Key Derivation Function 2 (PBKDF2) by * generating the key with the help of the functionality provided by * OpenSSL::PKCS5.pbkdf2_hmac_sha1 or OpenSSL::PKCS5.pbkdf2_hmac. * * Although there is Cipher#pkcs5_keyivgen, its use is deprecated and * it should only be used in legacy applications because it does not use * the newer PKCS#5 v2 algorithms. * * === Choosing an IV * * The cipher modes CBC, CFB, OFB and CTR all need an "initialization * vector", or short, IV. ECB mode is the only mode that does not require * an IV, but there is almost no legitimate use case for this mode * because of the fact that it does not sufficiently hide plaintext * patterns. Therefore * * You should never use ECB mode unless you are absolutely sure that * you absolutely need it * * Because of this, you will end up with a mode that explicitly requires * an IV in any case. Although the IV can be seen as public information, * i.e. it may be transmitted in public once generated, it should still * stay unpredictable to prevent certain kinds of attacks. Therefore, * ideally * * Always create a secure random IV for every encryption of your * Cipher * * A new, random IV should be created for every encryption of data. Think * of the IV as a nonce (number used once) - it's public but random and * unpredictable. A secure random IV can be created as follows * * cipher = ... * cipher.encrypt * key = cipher.random_key * iv = cipher.random_iv # also sets the generated IV on the Cipher * * Although the key is generally a random value, too, it is a bad choice * as an IV. There are elaborate ways how an attacker can take advantage * of such an IV. As a general rule of thumb, exposing the key directly * or indirectly should be avoided at all cost and exceptions only be * made with good reason. * * === Calling Cipher#final * * ECB (which should not be used) and CBC are both block-based modes. * This means that unlike for the other streaming-based modes, they * operate on fixed-size blocks of data, and therefore they require a * "finalization" step to produce or correctly decrypt the last block of * data by appropriately handling some form of padding. Therefore it is * essential to add the output of OpenSSL::Cipher#final to your * encryption/decryption buffer or you will end up with decryption errors * or truncated data. * * Although this is not really necessary for streaming-mode ciphers, it is * still recommended to apply the same pattern of adding the output of * Cipher#final there as well - it also enables you to switch between * modes more easily in the future. * * === Encrypting and decrypting some data * * data = "Very, very confidential data" * * cipher = OpenSSL::Cipher::AES.new(128, :CBC) * cipher.encrypt * key = cipher.random_key * iv = cipher.random_iv * * encrypted = cipher.update(data) + cipher.final * ... * decipher = OpenSSL::Cipher::AES.new(128, :CBC) * decipher.decrypt * decipher.key = key * decipher.iv = iv * * plain = decipher.update(encrypted) + decipher.final * * puts data == plain #=> true * * === Authenticated Encryption and Associated Data (AEAD) * * If the OpenSSL version used supports it, an Authenticated Encryption * mode (such as GCM or CCM) should always be preferred over any * unauthenticated mode. Currently, OpenSSL supports AE only in combination * with Associated Data (AEAD) where additional associated data is included * in the encryption process to compute a tag at the end of the encryption. * This tag will also be used in the decryption process and by verifying * its validity, the authenticity of a given ciphertext is established. * * This is superior to unauthenticated modes in that it allows to detect * if somebody effectively changed the ciphertext after it had been * encrypted. This prevents malicious modifications of the ciphertext that * could otherwise be exploited to modify ciphertexts in ways beneficial to * potential attackers. * * An associated data is used where there is additional information, such as * headers or some metadata, that must be also authenticated but not * necessarily need to be encrypted. If no associated data is needed for * encryption and later decryption, the OpenSSL library still requires a * value to be set - "" may be used in case none is available. * * An example using the GCM (Galois/Counter Mode). You have 16 bytes +key+, * 12 bytes (96 bits) +nonce+ and the associated data +auth_data+. Be sure * not to reuse the +key+ and +nonce+ pair. Reusing an nonce ruins the * security guarantees of GCM mode. * * cipher = OpenSSL::Cipher::AES.new(128, :GCM).encrypt * cipher.key = key * cipher.iv = nonce * cipher.auth_data = auth_data * * encrypted = cipher.update(data) + cipher.final * tag = cipher.auth_tag # produces 16 bytes tag by default * * Now you are the receiver. You know the +key+ and have received +nonce+, * +auth_data+, +encrypted+ and +tag+ through an untrusted network. Note * that GCM accepts an arbitrary length tag between 1 and 16 bytes. You may * additionally need to check that the received tag has the correct length, * or you allow attackers to forge a valid single byte tag for the tampered * ciphertext with a probability of 1/256. * * raise "tag is truncated!" unless tag.bytesize == 16 * decipher = OpenSSL::Cipher::AES.new(128, :GCM).decrypt * decipher.key = key * decipher.iv = nonce * decipher.auth_tag = tag * decipher.auth_data = auth_data * * decrypted = decipher.update(encrypted) + decipher.final * * puts data == decrypted #=> true */ cCipher = rb_define_class_under(mOSSL, "Cipher", rb_cObject); eCipherError = rb_define_class_under(cCipher, "CipherError", eOSSLError); rb_define_alloc_func(cCipher, ossl_cipher_alloc); rb_define_copy_func(cCipher, ossl_cipher_copy); rb_define_module_function(cCipher, "ciphers", ossl_s_ciphers, 0); rb_define_method(cCipher, "initialize", ossl_cipher_initialize, 1); rb_define_method(cCipher, "reset", ossl_cipher_reset, 0); rb_define_method(cCipher, "encrypt", ossl_cipher_encrypt, -1); rb_define_method(cCipher, "decrypt", ossl_cipher_decrypt, -1); rb_define_method(cCipher, "pkcs5_keyivgen", ossl_cipher_pkcs5_keyivgen, -1); rb_define_method(cCipher, "update", ossl_cipher_update, -1); rb_define_method(cCipher, "final", ossl_cipher_final, 0); rb_define_method(cCipher, "name", ossl_cipher_name, 0); rb_define_method(cCipher, "key=", ossl_cipher_set_key, 1); rb_define_method(cCipher, "auth_data=", ossl_cipher_set_auth_data, 1); rb_define_method(cCipher, "auth_tag=", ossl_cipher_set_auth_tag, 1); rb_define_method(cCipher, "auth_tag", ossl_cipher_get_auth_tag, -1); rb_define_method(cCipher, "auth_tag_len=", ossl_cipher_set_auth_tag_len, 1); rb_define_method(cCipher, "authenticated?", ossl_cipher_is_authenticated, 0); rb_define_method(cCipher, "key_len=", ossl_cipher_set_key_length, 1); rb_define_method(cCipher, "key_len", ossl_cipher_key_length, 0); rb_define_method(cCipher, "iv=", ossl_cipher_set_iv, 1); rb_define_method(cCipher, "iv_len=", ossl_cipher_set_iv_length, 1); rb_define_method(cCipher, "iv_len", ossl_cipher_iv_length, 0); rb_define_method(cCipher, "block_size", ossl_cipher_block_size, 0); rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1); id_auth_tag_len = rb_intern_const("auth_tag_len"); id_key_set = rb_intern_const("key_set"); } openssl-2.0.9/ext/openssl/ossl_cipher.h000066400000000000000000000007211336157045000201570ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_CIPHER_H_) #define _OSSL_CIPHER_H_ extern VALUE cCipher; extern VALUE eCipherError; const EVP_CIPHER *GetCipherPtr(VALUE); VALUE ossl_cipher_new(const EVP_CIPHER *); void Init_ossl_cipher(void); #endif /* _OSSL_CIPHER_H_ */ openssl-2.0.9/ext/openssl/ossl_config.c000066400000000000000000000037351336157045000201550ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" /* * Classes */ VALUE cConfig; /* Document-class: OpenSSL::ConfigError * * General error for openssl library configuration files. Including formatting, * parsing errors, etc. */ VALUE eConfigError; /* * Public */ /* * DupConfigPtr is a public C-level function for getting OpenSSL CONF struct * from an OpenSSL::Config(eConfig) instance. We decided to implement * OpenSSL::Config in Ruby level but we need to pass native CONF struct for * some OpenSSL features such as X509V3_EXT_*. */ CONF * DupConfigPtr(VALUE obj) { CONF *conf; VALUE str; BIO *bio; long eline = -1; OSSL_Check_Kind(obj, cConfig); str = rb_funcall(obj, rb_intern("to_s"), 0); bio = ossl_obj2bio(&str); conf = NCONF_new(NULL); if(!conf){ BIO_free(bio); ossl_raise(eConfigError, NULL); } if(!NCONF_load_bio(conf, bio, &eline)){ BIO_free(bio); NCONF_free(conf); if (eline <= 0) ossl_raise(eConfigError, "wrong config format"); else ossl_raise(eConfigError, "error in line %d", eline); } BIO_free(bio); return conf; } /* Document-const: DEFAULT_CONFIG_FILE * * The default system configuration file for openssl */ /* * INIT */ void Init_ossl_config(void) { char *default_config_file; #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif eConfigError = rb_define_class_under(mOSSL, "ConfigError", eOSSLError); cConfig = rb_define_class_under(mOSSL, "Config", rb_cObject); default_config_file = CONF_get1_default_config_file(); rb_define_const(cConfig, "DEFAULT_CONFIG_FILE", rb_str_new2(default_config_file)); OPENSSL_free(default_config_file); /* methods are defined by openssl/config.rb */ } openssl-2.0.9/ext/openssl/ossl_config.h000066400000000000000000000006361336157045000201570ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_CONFIG_H_) #define _OSSL_CONFIG_H_ extern VALUE cConfig; extern VALUE eConfigError; CONF* DupConfigPtr(VALUE obj); void Init_ossl_config(void); #endif /* _OSSL_CONFIG_H_ */ openssl-2.0.9/ext/openssl/ossl_digest.c000066400000000000000000000276171336157045000201740ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define GetDigest(obj, ctx) do { \ TypedData_Get_Struct((obj), EVP_MD_CTX, &ossl_digest_type, (ctx)); \ if (!(ctx)) { \ ossl_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \ } \ } while (0) #define SafeGetDigest(obj, ctx) do { \ OSSL_Check_Kind((obj), cDigest); \ GetDigest((obj), (ctx)); \ } while (0) /* * Classes */ VALUE cDigest; VALUE eDigestError; static VALUE ossl_digest_alloc(VALUE klass); static void ossl_digest_free(void *ctx) { EVP_MD_CTX_destroy(ctx); } static const rb_data_type_t ossl_digest_type = { "OpenSSL/Digest", { 0, ossl_digest_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * Public */ const EVP_MD * GetDigestPtr(VALUE obj) { const EVP_MD *md; ASN1_OBJECT *oid = NULL; if (RB_TYPE_P(obj, T_STRING)) { const char *name = StringValueCStr(obj); md = EVP_get_digestbyname(name); if (!md) { oid = OBJ_txt2obj(name, 0); md = EVP_get_digestbyobj(oid); ASN1_OBJECT_free(oid); } if(!md) ossl_raise(rb_eRuntimeError, "Unsupported digest algorithm (%"PRIsVALUE").", obj); } else { EVP_MD_CTX *ctx; SafeGetDigest(obj, ctx); md = EVP_MD_CTX_md(ctx); } return md; } VALUE ossl_digest_new(const EVP_MD *md) { VALUE ret; EVP_MD_CTX *ctx; ret = ossl_digest_alloc(cDigest); ctx = EVP_MD_CTX_new(); if (!ctx) ossl_raise(eDigestError, "EVP_MD_CTX_new"); RTYPEDDATA_DATA(ret) = ctx; if (!EVP_DigestInit_ex(ctx, md, NULL)) ossl_raise(eDigestError, "Digest initialization failed"); return ret; } /* * Private */ static VALUE ossl_digest_alloc(VALUE klass) { return TypedData_Wrap_Struct(klass, &ossl_digest_type, 0); } VALUE ossl_digest_update(VALUE, VALUE); /* * call-seq: * Digest.new(string [, data]) -> Digest * * Creates a Digest instance based on +string+, which is either the ln * (long name) or sn (short name) of a supported digest algorithm. * * If +data+ (a +String+) is given, it is used as the initial input to the * Digest instance, i.e. * * digest = OpenSSL::Digest.new('sha256', 'digestdata') * * is equal to * * digest = OpenSSL::Digest.new('sha256') * digest.update('digestdata') */ static VALUE ossl_digest_initialize(int argc, VALUE *argv, VALUE self) { EVP_MD_CTX *ctx; const EVP_MD *md; VALUE type, data; rb_scan_args(argc, argv, "11", &type, &data); md = GetDigestPtr(type); if (!NIL_P(data)) StringValue(data); TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx); if (!ctx) { RTYPEDDATA_DATA(self) = ctx = EVP_MD_CTX_new(); if (!ctx) ossl_raise(eDigestError, "EVP_MD_CTX_new"); } if (!EVP_DigestInit_ex(ctx, md, NULL)) ossl_raise(eDigestError, "Digest initialization failed"); if (!NIL_P(data)) return ossl_digest_update(self, data); return self; } static VALUE ossl_digest_copy(VALUE self, VALUE other) { EVP_MD_CTX *ctx1, *ctx2; rb_check_frozen(self); if (self == other) return self; TypedData_Get_Struct(self, EVP_MD_CTX, &ossl_digest_type, ctx1); if (!ctx1) { RTYPEDDATA_DATA(self) = ctx1 = EVP_MD_CTX_new(); if (!ctx1) ossl_raise(eDigestError, "EVP_MD_CTX_new"); } SafeGetDigest(other, ctx2); if (!EVP_MD_CTX_copy(ctx1, ctx2)) { ossl_raise(eDigestError, NULL); } return self; } /* * call-seq: * digest.reset -> self * * Resets the Digest in the sense that any Digest#update that has been * performed is abandoned and the Digest is set to its initial state again. * */ static VALUE ossl_digest_reset(VALUE self) { EVP_MD_CTX *ctx; GetDigest(self, ctx); if (EVP_DigestInit_ex(ctx, EVP_MD_CTX_md(ctx), NULL) != 1) { ossl_raise(eDigestError, "Digest initialization failed."); } return self; } /* * call-seq: * digest.update(string) -> aString * * Not every message digest can be computed in one single pass. If a message * digest is to be computed from several subsequent sources, then each may * be passed individually to the Digest instance. * * === Example * digest = OpenSSL::Digest::SHA256.new * digest.update('First input') * digest << 'Second input' # equivalent to digest.update('Second input') * result = digest.digest * */ VALUE ossl_digest_update(VALUE self, VALUE data) { EVP_MD_CTX *ctx; StringValue(data); GetDigest(self, ctx); if (!EVP_DigestUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) ossl_raise(eDigestError, "EVP_DigestUpdate"); return self; } /* * call-seq: * digest.finish -> aString * */ static VALUE ossl_digest_finish(int argc, VALUE *argv, VALUE self) { EVP_MD_CTX *ctx; VALUE str; int out_len; GetDigest(self, ctx); rb_scan_args(argc, argv, "01", &str); out_len = EVP_MD_CTX_size(ctx); if (NIL_P(str)) { str = rb_str_new(NULL, out_len); } else { StringValue(str); rb_str_resize(str, out_len); } if (!EVP_DigestFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), NULL)) ossl_raise(eDigestError, "EVP_DigestFinal_ex"); return str; } /* * call-seq: * digest.name -> string * * Returns the sn of this Digest algorithm. * * === Example * digest = OpenSSL::Digest::SHA512.new * puts digest.name # => SHA512 * */ static VALUE ossl_digest_name(VALUE self) { EVP_MD_CTX *ctx; GetDigest(self, ctx); return rb_str_new2(EVP_MD_name(EVP_MD_CTX_md(ctx))); } /* * call-seq: * digest.digest_length -> integer * * Returns the output size of the digest, i.e. the length in bytes of the * final message digest result. * * === Example * digest = OpenSSL::Digest::SHA1.new * puts digest.digest_length # => 20 * */ static VALUE ossl_digest_size(VALUE self) { EVP_MD_CTX *ctx; GetDigest(self, ctx); return INT2NUM(EVP_MD_CTX_size(ctx)); } /* * call-seq: * digest.block_length -> integer * * Returns the block length of the digest algorithm, i.e. the length in bytes * of an individual block. Most modern algorithms partition a message to be * digested into a sequence of fix-sized blocks that are processed * consecutively. * * === Example * digest = OpenSSL::Digest::SHA1.new * puts digest.block_length # => 64 */ static VALUE ossl_digest_block_length(VALUE self) { EVP_MD_CTX *ctx; GetDigest(self, ctx); return INT2NUM(EVP_MD_CTX_block_size(ctx)); } /* * INIT */ void Init_ossl_digest(void) { rb_require("digest"); #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif /* Document-class: OpenSSL::Digest * * OpenSSL::Digest allows you to compute message digests (sometimes * interchangeably called "hashes") of arbitrary data that are * cryptographically secure, i.e. a Digest implements a secure one-way * function. * * One-way functions offer some useful properties. E.g. given two * distinct inputs the probability that both yield the same output * is highly unlikely. Combined with the fact that every message digest * algorithm has a fixed-length output of just a few bytes, digests are * often used to create unique identifiers for arbitrary data. A common * example is the creation of a unique id for binary documents that are * stored in a database. * * Another useful characteristic of one-way functions (and thus the name) * is that given a digest there is no indication about the original * data that produced it, i.e. the only way to identify the original input * is to "brute-force" through every possible combination of inputs. * * These characteristics make one-way functions also ideal companions * for public key signature algorithms: instead of signing an entire * document, first a hash of the document is produced with a considerably * faster message digest algorithm and only the few bytes of its output * need to be signed using the slower public key algorithm. To validate * the integrity of a signed document, it suffices to re-compute the hash * and verify that it is equal to that in the signature. * * Among the supported message digest algorithms are: * * SHA, SHA1, SHA224, SHA256, SHA384 and SHA512 * * MD2, MD4, MDC2 and MD5 * * RIPEMD160 * * DSS, DSS1 (Pseudo algorithms to be used for DSA signatures. DSS is * equal to SHA and DSS1 is equal to SHA1) * * For each of these algorithms, there is a sub-class of Digest that * can be instantiated as simply as e.g. * * digest = OpenSSL::Digest::SHA1.new * * === Mapping between Digest class and sn/ln * * The sn (short names) and ln (long names) are defined in * and . They are textual * representations of ASN.1 OBJECT IDENTIFIERs. Each supported digest * algorithm has an OBJECT IDENTIFIER associated to it and those again * have short/long names assigned to them. * E.g. the OBJECT IDENTIFIER for SHA-1 is 1.3.14.3.2.26 and its * sn is "SHA1" and its ln is "sha1". * ==== MD2 * * sn: MD2 * * ln: md2 * ==== MD4 * * sn: MD4 * * ln: md4 * ==== MD5 * * sn: MD5 * * ln: md5 * ==== SHA * * sn: SHA * * ln: SHA * ==== SHA-1 * * sn: SHA1 * * ln: sha1 * ==== SHA-224 * * sn: SHA224 * * ln: sha224 * ==== SHA-256 * * sn: SHA256 * * ln: sha256 * ==== SHA-384 * * sn: SHA384 * * ln: sha384 * ==== SHA-512 * * sn: SHA512 * * ln: sha512 * * "Breaking" a message digest algorithm means defying its one-way * function characteristics, i.e. producing a collision or finding a way * to get to the original data by means that are more efficient than * brute-forcing etc. Most of the supported digest algorithms can be * considered broken in this sense, even the very popular MD5 and SHA1 * algorithms. Should security be your highest concern, then you should * probably rely on SHA224, SHA256, SHA384 or SHA512. * * === Hashing a file * * data = File.read('document') * sha256 = OpenSSL::Digest::SHA256.new * digest = sha256.digest(data) * * === Hashing several pieces of data at once * * data1 = File.read('file1') * data2 = File.read('file2') * data3 = File.read('file3') * sha256 = OpenSSL::Digest::SHA256.new * sha256 << data1 * sha256 << data2 * sha256 << data3 * digest = sha256.digest * * === Reuse a Digest instance * * data1 = File.read('file1') * sha256 = OpenSSL::Digest::SHA256.new * digest1 = sha256.digest(data1) * * data2 = File.read('file2') * sha256.reset * digest2 = sha256.digest(data2) * */ cDigest = rb_define_class_under(mOSSL, "Digest", rb_path2class("Digest::Class")); /* Document-class: OpenSSL::Digest::DigestError * * Generic Exception class that is raised if an error occurs during a * Digest operation. */ eDigestError = rb_define_class_under(cDigest, "DigestError", eOSSLError); rb_define_alloc_func(cDigest, ossl_digest_alloc); rb_define_method(cDigest, "initialize", ossl_digest_initialize, -1); rb_define_copy_func(cDigest, ossl_digest_copy); rb_define_method(cDigest, "reset", ossl_digest_reset, 0); rb_define_method(cDigest, "update", ossl_digest_update, 1); rb_define_alias(cDigest, "<<", "update"); rb_define_private_method(cDigest, "finish", ossl_digest_finish, -1); rb_define_method(cDigest, "digest_length", ossl_digest_size, 0); rb_define_method(cDigest, "block_length", ossl_digest_block_length, 0); rb_define_method(cDigest, "name", ossl_digest_name, 0); } openssl-2.0.9/ext/openssl/ossl_digest.h000066400000000000000000000007111336157045000201630ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_DIGEST_H_) #define _OSSL_DIGEST_H_ extern VALUE cDigest; extern VALUE eDigestError; const EVP_MD *GetDigestPtr(VALUE); VALUE ossl_digest_new(const EVP_MD *); void Init_ossl_digest(void); #endif /* _OSSL_DIGEST_H_ */ openssl-2.0.9/ext/openssl/ossl_engine.c000066400000000000000000000340461336157045000201540ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2003 GOTOU Yuuzou * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #if !defined(OPENSSL_NO_ENGINE) #define NewEngine(klass) \ TypedData_Wrap_Struct((klass), &ossl_engine_type, 0) #define SetEngine(obj, engine) do { \ if (!(engine)) { \ ossl_raise(rb_eRuntimeError, "ENGINE wasn't initialized."); \ } \ RTYPEDDATA_DATA(obj) = (engine); \ } while(0) #define GetEngine(obj, engine) do { \ TypedData_Get_Struct((obj), ENGINE, &ossl_engine_type, (engine)); \ if (!(engine)) { \ ossl_raise(rb_eRuntimeError, "ENGINE wasn't initialized."); \ } \ } while (0) #define SafeGetEngine(obj, engine) do { \ OSSL_Check_Kind((obj), cEngine); \ GetPKCS7((obj), (engine)); \ } while (0) /* * Classes */ /* Document-class: OpenSSL::Engine * * This class is the access to openssl's ENGINE cryptographic module * implementation. * * See also, https://www.openssl.org/docs/crypto/engine.html */ VALUE cEngine; /* Document-class: OpenSSL::Engine::EngineError * * This is the generic exception for OpenSSL::Engine related errors */ VALUE eEngineError; /* * Private */ #define OSSL_ENGINE_LOAD_IF_MATCH(x) \ do{\ if(!strcmp(#x, RSTRING_PTR(name))){\ ENGINE_load_##x();\ return Qtrue;\ }\ }while(0) static void ossl_engine_free(void *engine) { ENGINE_free(engine); } static const rb_data_type_t ossl_engine_type = { "OpenSSL/Engine", { 0, ossl_engine_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* Document-method: OpenSSL::Engine.load * * call-seq: * load(enginename = nil) * * This method loads engines. If +name+ is nil, then all builtin engines are * loaded. Otherwise, the given +name+, as a string, is loaded if available to * your runtime, and returns true. If +name+ is not found, then nil is * returned. * */ static VALUE ossl_engine_s_load(int argc, VALUE *argv, VALUE klass) { #if !defined(HAVE_ENGINE_LOAD_BUILTIN_ENGINES) return Qnil; #else VALUE name; rb_scan_args(argc, argv, "01", &name); if(NIL_P(name)){ ENGINE_load_builtin_engines(); return Qtrue; } StringValueCStr(name); #ifndef OPENSSL_NO_STATIC_ENGINE #if HAVE_ENGINE_LOAD_DYNAMIC OSSL_ENGINE_LOAD_IF_MATCH(dynamic); #endif #if HAVE_ENGINE_LOAD_4758CCA OSSL_ENGINE_LOAD_IF_MATCH(4758cca); #endif #if HAVE_ENGINE_LOAD_AEP OSSL_ENGINE_LOAD_IF_MATCH(aep); #endif #if HAVE_ENGINE_LOAD_ATALLA OSSL_ENGINE_LOAD_IF_MATCH(atalla); #endif #if HAVE_ENGINE_LOAD_CHIL OSSL_ENGINE_LOAD_IF_MATCH(chil); #endif #if HAVE_ENGINE_LOAD_CSWIFT OSSL_ENGINE_LOAD_IF_MATCH(cswift); #endif #if HAVE_ENGINE_LOAD_NURON OSSL_ENGINE_LOAD_IF_MATCH(nuron); #endif #if HAVE_ENGINE_LOAD_SUREWARE OSSL_ENGINE_LOAD_IF_MATCH(sureware); #endif #if HAVE_ENGINE_LOAD_UBSEC OSSL_ENGINE_LOAD_IF_MATCH(ubsec); #endif #if HAVE_ENGINE_LOAD_PADLOCK OSSL_ENGINE_LOAD_IF_MATCH(padlock); #endif #if HAVE_ENGINE_LOAD_CAPI OSSL_ENGINE_LOAD_IF_MATCH(capi); #endif #if HAVE_ENGINE_LOAD_GMP OSSL_ENGINE_LOAD_IF_MATCH(gmp); #endif #if HAVE_ENGINE_LOAD_GOST OSSL_ENGINE_LOAD_IF_MATCH(gost); #endif #if HAVE_ENGINE_LOAD_CRYPTODEV OSSL_ENGINE_LOAD_IF_MATCH(cryptodev); #endif #if HAVE_ENGINE_LOAD_AESNI OSSL_ENGINE_LOAD_IF_MATCH(aesni); #endif #endif #ifdef HAVE_ENGINE_LOAD_OPENBSD_DEV_CRYPTO OSSL_ENGINE_LOAD_IF_MATCH(openbsd_dev_crypto); #endif OSSL_ENGINE_LOAD_IF_MATCH(openssl); rb_warning("no such builtin loader for `%"PRIsVALUE"'", name); return Qnil; #endif /* HAVE_ENGINE_LOAD_BUILTIN_ENGINES */ } /* Document-method: OpenSSL::Engine.cleanup * call-seq: * OpenSSL::Engine.cleanup * * It is only necessary to run cleanup when engines are loaded via * OpenSSL::Engine.load. However, running cleanup before exit is recommended. * * Note that this is needed and works only in OpenSSL < 1.1.0. */ static VALUE ossl_engine_s_cleanup(VALUE self) { ENGINE_cleanup(); return Qnil; } /* Document-method: OpenSSL::Engine.engines * * Returns an array of currently loaded engines. */ static VALUE ossl_engine_s_engines(VALUE klass) { ENGINE *e; VALUE ary, obj; ary = rb_ary_new(); for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)){ obj = NewEngine(klass); /* Need a ref count of two here because of ENGINE_free being * called internally by OpenSSL when moving to the next ENGINE * and by us when releasing the ENGINE reference */ ENGINE_up_ref(e); SetEngine(obj, e); rb_ary_push(ary, obj); } return ary; } /* Document-method: OpenSSL::Engine.by_id * * call-seq: * by_id(name) -> engine * * Fetch the engine as specified by the +id+ String * * OpenSSL::Engine.by_id("openssl") * => # * * See OpenSSL::Engine.engines for the currently loaded engines */ static VALUE ossl_engine_s_by_id(VALUE klass, VALUE id) { ENGINE *e; VALUE obj; StringValueCStr(id); ossl_engine_s_load(1, &id, klass); obj = NewEngine(klass); if(!(e = ENGINE_by_id(RSTRING_PTR(id)))) ossl_raise(eEngineError, NULL); SetEngine(obj, e); if(rb_block_given_p()) rb_yield(obj); if(!ENGINE_init(e)) ossl_raise(eEngineError, NULL); ENGINE_ctrl(e, ENGINE_CTRL_SET_PASSWORD_CALLBACK, 0, NULL, (void(*)(void))ossl_pem_passwd_cb); ossl_clear_error(); return obj; } /* Document-method: OpenSSL::Engine#id * * Get the id for this engine * * OpenSSL::Engine.load * OpenSSL::Engine.engines #=> [#, ...] * OpenSSL::Engine.engines.first.id * #=> "rsax" */ static VALUE ossl_engine_get_id(VALUE self) { ENGINE *e; GetEngine(self, e); return rb_str_new2(ENGINE_get_id(e)); } /* Document-method: OpenSSL::Engine#name * * Get the descriptive name for this engine * * OpenSSL::Engine.load * OpenSSL::Engine.engines #=> [#, ...] * OpenSSL::Engine.engines.first.name * #=> "RSAX engine support" * */ static VALUE ossl_engine_get_name(VALUE self) { ENGINE *e; GetEngine(self, e); return rb_str_new2(ENGINE_get_name(e)); } /* Document-method: OpenSSL::Engine#finish * * Releases all internal structural references for this engine. * * May raise an EngineError if the engine is unavailable */ static VALUE ossl_engine_finish(VALUE self) { ENGINE *e; GetEngine(self, e); if(!ENGINE_finish(e)) ossl_raise(eEngineError, NULL); return Qnil; } /* Document-method: OpenSSL::Engine#cipher * * call-seq: * engine.cipher(name) -> OpenSSL::Cipher * * This returns an OpenSSL::Cipher by +name+, if it is available in this * engine. * * An EngineError will be raised if the cipher is unavailable. * * e = OpenSSL::Engine.by_id("openssl") * => # * e.cipher("RC4") * => # * */ static VALUE ossl_engine_get_cipher(VALUE self, VALUE name) { ENGINE *e; const EVP_CIPHER *ciph, *tmp; int nid; tmp = EVP_get_cipherbyname(StringValueCStr(name)); if(!tmp) ossl_raise(eEngineError, "no such cipher `%"PRIsVALUE"'", name); nid = EVP_CIPHER_nid(tmp); GetEngine(self, e); ciph = ENGINE_get_cipher(e, nid); if(!ciph) ossl_raise(eEngineError, NULL); return ossl_cipher_new(ciph); } /* Document-method: OpenSSL::Engine#digest * * call-seq: * engine.digest(name) -> OpenSSL::Digest * * This returns an OpenSSL::Digest by +name+. * * Will raise an EngineError if the digest is unavailable. * * e = OpenSSL::Engine.by_id("openssl") * #=> # * e.digest("SHA1") * #=> # * e.digest("zomg") * #=> OpenSSL::Engine::EngineError: no such digest `zomg' */ static VALUE ossl_engine_get_digest(VALUE self, VALUE name) { ENGINE *e; const EVP_MD *md, *tmp; int nid; tmp = EVP_get_digestbyname(StringValueCStr(name)); if(!tmp) ossl_raise(eEngineError, "no such digest `%"PRIsVALUE"'", name); nid = EVP_MD_nid(tmp); GetEngine(self, e); md = ENGINE_get_digest(e, nid); if(!md) ossl_raise(eEngineError, NULL); return ossl_digest_new(md); } /* Document-method: OpenSSL::Engine#load_private_key * * call-seq: * engine.load_private_key(id = nil, data = nil) -> OpenSSL::PKey * * Loads the given private key by +id+ and +data+. * * An EngineError is raised of the OpenSSL::PKey is unavailable. * */ static VALUE ossl_engine_load_privkey(int argc, VALUE *argv, VALUE self) { ENGINE *e; EVP_PKEY *pkey; VALUE id, data, obj; char *sid, *sdata; rb_scan_args(argc, argv, "02", &id, &data); sid = NIL_P(id) ? NULL : StringValueCStr(id); sdata = NIL_P(data) ? NULL : StringValueCStr(data); GetEngine(self, e); pkey = ENGINE_load_private_key(e, sid, NULL, sdata); if (!pkey) ossl_raise(eEngineError, NULL); obj = ossl_pkey_new(pkey); OSSL_PKEY_SET_PRIVATE(obj); return obj; } /* Document-method: OpenSSL::Engine#load_public_key * * call-seq: * engine.load_public_key(id = nil, data = nil) -> OpenSSL::PKey * * Loads the given private key by +id+ and +data+. * * An EngineError is raised of the OpenSSL::PKey is unavailable. * */ static VALUE ossl_engine_load_pubkey(int argc, VALUE *argv, VALUE self) { ENGINE *e; EVP_PKEY *pkey; VALUE id, data; char *sid, *sdata; rb_scan_args(argc, argv, "02", &id, &data); sid = NIL_P(id) ? NULL : StringValueCStr(id); sdata = NIL_P(data) ? NULL : StringValueCStr(data); GetEngine(self, e); pkey = ENGINE_load_public_key(e, sid, NULL, sdata); if (!pkey) ossl_raise(eEngineError, NULL); return ossl_pkey_new(pkey); } /* Document-method: OpenSSL::Engine#set_default * * call-seq: * engine.set_default(flag) * * Set the defaults for this engine with the given +flag+. * * These flags are used to control combinations of algorithm methods. * * +flag+ can be one of the following, other flags are available depending on * your OS. * * [All flags] 0xFFFF * [No flags] 0x0000 * * See also */ static VALUE ossl_engine_set_default(VALUE self, VALUE flag) { ENGINE *e; int f = NUM2INT(flag); GetEngine(self, e); ENGINE_set_default(e, f); return Qtrue; } /* Document-method: OpenSSL::Engine#ctrl_cmd * * call-seq: * engine.ctrl_cmd(command, value = nil) -> engine * * Send the given +command+ to this engine. * * Raises an EngineError if the +command+ fails. */ static VALUE ossl_engine_ctrl_cmd(int argc, VALUE *argv, VALUE self) { ENGINE *e; VALUE cmd, val; int ret; GetEngine(self, e); rb_scan_args(argc, argv, "11", &cmd, &val); ret = ENGINE_ctrl_cmd_string(e, StringValueCStr(cmd), NIL_P(val) ? NULL : StringValueCStr(val), 0); if (!ret) ossl_raise(eEngineError, NULL); return self; } static VALUE ossl_engine_cmd_flag_to_name(int flag) { switch(flag){ case ENGINE_CMD_FLAG_NUMERIC: return rb_str_new2("NUMERIC"); case ENGINE_CMD_FLAG_STRING: return rb_str_new2("STRING"); case ENGINE_CMD_FLAG_NO_INPUT: return rb_str_new2("NO_INPUT"); case ENGINE_CMD_FLAG_INTERNAL: return rb_str_new2("INTERNAL"); default: return rb_str_new2("UNKNOWN"); } } /* Document-method: OpenSSL::Engine#cmds * * Returns an array of command definitions for the current engine */ static VALUE ossl_engine_get_cmds(VALUE self) { ENGINE *e; const ENGINE_CMD_DEFN *defn, *p; VALUE ary, tmp; GetEngine(self, e); ary = rb_ary_new(); if ((defn = ENGINE_get_cmd_defns(e)) != NULL){ for (p = defn; p->cmd_num > 0; p++){ tmp = rb_ary_new(); rb_ary_push(tmp, rb_str_new2(p->cmd_name)); rb_ary_push(tmp, rb_str_new2(p->cmd_desc)); rb_ary_push(tmp, ossl_engine_cmd_flag_to_name(p->cmd_flags)); rb_ary_push(ary, tmp); } } return ary; } /* Document-method: OpenSSL::Engine#inspect * * Pretty print this engine */ static VALUE ossl_engine_inspect(VALUE self) { ENGINE *e; GetEngine(self, e); return rb_sprintf("#<%"PRIsVALUE" id=\"%s\" name=\"%s\">", rb_obj_class(self), ENGINE_get_id(e), ENGINE_get_name(e)); } #define DefEngineConst(x) rb_define_const(cEngine, #x, INT2NUM(ENGINE_##x)) void Init_ossl_engine(void) { #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif cEngine = rb_define_class_under(mOSSL, "Engine", rb_cObject); eEngineError = rb_define_class_under(cEngine, "EngineError", eOSSLError); rb_undef_alloc_func(cEngine); rb_define_singleton_method(cEngine, "load", ossl_engine_s_load, -1); rb_define_singleton_method(cEngine, "cleanup", ossl_engine_s_cleanup, 0); rb_define_singleton_method(cEngine, "engines", ossl_engine_s_engines, 0); rb_define_singleton_method(cEngine, "by_id", ossl_engine_s_by_id, 1); rb_define_method(cEngine, "id", ossl_engine_get_id, 0); rb_define_method(cEngine, "name", ossl_engine_get_name, 0); rb_define_method(cEngine, "finish", ossl_engine_finish, 0); rb_define_method(cEngine, "cipher", ossl_engine_get_cipher, 1); rb_define_method(cEngine, "digest", ossl_engine_get_digest, 1); rb_define_method(cEngine, "load_private_key", ossl_engine_load_privkey, -1); rb_define_method(cEngine, "load_public_key", ossl_engine_load_pubkey, -1); rb_define_method(cEngine, "set_default", ossl_engine_set_default, 1); rb_define_method(cEngine, "ctrl_cmd", ossl_engine_ctrl_cmd, -1); rb_define_method(cEngine, "cmds", ossl_engine_get_cmds, 0); rb_define_method(cEngine, "inspect", ossl_engine_inspect, 0); DefEngineConst(METHOD_RSA); DefEngineConst(METHOD_DSA); DefEngineConst(METHOD_DH); DefEngineConst(METHOD_RAND); #ifdef ENGINE_METHOD_BN_MOD_EXP DefEngineConst(METHOD_BN_MOD_EXP); #endif #ifdef ENGINE_METHOD_BN_MOD_EXP_CRT DefEngineConst(METHOD_BN_MOD_EXP_CRT); #endif DefEngineConst(METHOD_CIPHERS); DefEngineConst(METHOD_DIGESTS); DefEngineConst(METHOD_ALL); DefEngineConst(METHOD_NONE); } #else void Init_ossl_engine(void) { } #endif openssl-2.0.9/ext/openssl/ossl_engine.h000066400000000000000000000006571336157045000201620ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2003 Michal Rokos * Copyright (C) 2003 GOTOU Yuuzou * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(OSSL_ENGINE_H) #define OSSL_ENGINE_H extern VALUE cEngine; extern VALUE eEngineError; void Init_ossl_engine(void); #endif /* OSSL_ENGINE_H */ openssl-2.0.9/ext/openssl/ossl_hmac.c000066400000000000000000000230511336157045000176110ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(OPENSSL_NO_HMAC) #include "ossl.h" #define NewHMAC(klass) \ TypedData_Wrap_Struct((klass), &ossl_hmac_type, 0) #define GetHMAC(obj, ctx) do { \ TypedData_Get_Struct((obj), HMAC_CTX, &ossl_hmac_type, (ctx)); \ if (!(ctx)) { \ ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \ } \ } while (0) #define SafeGetHMAC(obj, ctx) do { \ OSSL_Check_Kind((obj), cHMAC); \ GetHMAC((obj), (ctx)); \ } while (0) /* * Classes */ VALUE cHMAC; VALUE eHMACError; /* * Public */ /* * Private */ static void ossl_hmac_free(void *ctx) { HMAC_CTX_free(ctx); } static const rb_data_type_t ossl_hmac_type = { "OpenSSL/HMAC", { 0, ossl_hmac_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static VALUE ossl_hmac_alloc(VALUE klass) { VALUE obj; HMAC_CTX *ctx; obj = NewHMAC(klass); ctx = HMAC_CTX_new(); if (!ctx) ossl_raise(eHMACError, NULL); RTYPEDDATA_DATA(obj) = ctx; return obj; } /* * call-seq: * HMAC.new(key, digest) -> hmac * * Returns an instance of OpenSSL::HMAC set with the key and digest * algorithm to be used. The instance represents the initial state of * the message authentication code before any data has been processed. * To process data with it, use the instance method #update with your * data as an argument. * * === Example * * key = 'key' * digest = OpenSSL::Digest.new('sha1') * instance = OpenSSL::HMAC.new(key, digest) * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * instance.class * #=> OpenSSL::HMAC * * === A note about comparisons * * Two instances won't be equal when they're compared, even if they have the * same value. Use #to_s or #hexdigest to return the authentication code that * the instance represents. For example: * * other_instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1')) * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * instance * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * instance == other_instance * #=> false * instance.to_s == other_instance.to_s * #=> true * */ static VALUE ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest) { HMAC_CTX *ctx; StringValue(key); GetHMAC(self, ctx); HMAC_Init_ex(ctx, RSTRING_PTR(key), RSTRING_LENINT(key), GetDigestPtr(digest), NULL); return self; } static VALUE ossl_hmac_copy(VALUE self, VALUE other) { HMAC_CTX *ctx1, *ctx2; rb_check_frozen(self); if (self == other) return self; GetHMAC(self, ctx1); SafeGetHMAC(other, ctx2); if (!HMAC_CTX_copy(ctx1, ctx2)) ossl_raise(eHMACError, "HMAC_CTX_copy"); return self; } /* * call-seq: * hmac.update(string) -> self * * Returns +self+ updated with the message to be authenticated. * Can be called repeatedly with chunks of the message. * * === Example * * first_chunk = 'The quick brown fox jumps ' * second_chunk = 'over the lazy dog' * * instance.update(first_chunk) * #=> 5b9a8038a65d571076d97fe783989e52278a492a * instance.update(second_chunk) * #=> de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9 * */ static VALUE ossl_hmac_update(VALUE self, VALUE data) { HMAC_CTX *ctx; StringValue(data); GetHMAC(self, ctx); HMAC_Update(ctx, (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data)); return self; } static void hmac_final(HMAC_CTX *ctx, unsigned char *buf, unsigned int *buf_len) { HMAC_CTX *final; final = HMAC_CTX_new(); if (!final) ossl_raise(eHMACError, "HMAC_CTX_new"); if (!HMAC_CTX_copy(final, ctx)) { HMAC_CTX_free(final); ossl_raise(eHMACError, "HMAC_CTX_copy"); } HMAC_Final(final, buf, buf_len); HMAC_CTX_free(final); } /* * call-seq: * hmac.digest -> string * * Returns the authentication code an instance represents as a binary string. * * === Example * instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1')) * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * instance.digest * #=> "\xF4+\xB0\xEE\xB0\x18\xEB\xBDE\x97\xAEr\x13q\x1E\xC6\a`\x84?" */ static VALUE ossl_hmac_digest(VALUE self) { HMAC_CTX *ctx; unsigned int buf_len; VALUE ret; GetHMAC(self, ctx); ret = rb_str_new(NULL, EVP_MAX_MD_SIZE); hmac_final(ctx, (unsigned char *)RSTRING_PTR(ret), &buf_len); assert(buf_len <= EVP_MAX_MD_SIZE); rb_str_set_len(ret, buf_len); return ret; } /* * call-seq: * hmac.hexdigest -> string * * Returns the authentication code an instance represents as a hex-encoded * string. */ static VALUE ossl_hmac_hexdigest(VALUE self) { HMAC_CTX *ctx; unsigned char buf[EVP_MAX_MD_SIZE]; unsigned int buf_len; VALUE ret; GetHMAC(self, ctx); hmac_final(ctx, buf, &buf_len); ret = rb_str_new(NULL, buf_len * 2); ossl_bin2hex(buf, RSTRING_PTR(ret), buf_len); return ret; } /* * call-seq: * hmac.reset -> self * * Returns +self+ as it was when it was first initialized, with all processed * data cleared from it. * * === Example * * data = "The quick brown fox jumps over the lazy dog" * instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1')) * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * * instance.update(data) * #=> de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9 * instance.reset * #=> f42bb0eeb018ebbd4597ae7213711ec60760843f * */ static VALUE ossl_hmac_reset(VALUE self) { HMAC_CTX *ctx; GetHMAC(self, ctx); HMAC_Init_ex(ctx, NULL, 0, NULL, NULL); return self; } /* * call-seq: * HMAC.digest(digest, key, data) -> aString * * Returns the authentication code as a binary string. The +digest+ parameter * must be an instance of OpenSSL::Digest. * * === Example * * key = 'key' * data = 'The quick brown fox jumps over the lazy dog' * digest = OpenSSL::Digest.new('sha1') * * hmac = OpenSSL::HMAC.digest(digest, key, data) * #=> "\xDE|\x9B\x85\xB8\xB7\x8A\xA6\xBC\x8Az6\xF7\n\x90p\x1C\x9D\xB4\xD9" * */ static VALUE ossl_hmac_s_digest(VALUE klass, VALUE digest, VALUE key, VALUE data) { unsigned char *buf; unsigned int buf_len; StringValue(key); StringValue(data); buf = HMAC(GetDigestPtr(digest), RSTRING_PTR(key), RSTRING_LENINT(key), (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data), NULL, &buf_len); return rb_str_new((const char *)buf, buf_len); } /* * call-seq: * HMAC.hexdigest(digest, key, data) -> aString * * Returns the authentication code as a hex-encoded string. The +digest+ * parameter must be an instance of OpenSSL::Digest. * * === Example * * key = 'key' * data = 'The quick brown fox jumps over the lazy dog' * digest = OpenSSL::Digest.new('sha1') * * hmac = OpenSSL::HMAC.hexdigest(digest, key, data) * #=> "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9" * */ static VALUE ossl_hmac_s_hexdigest(VALUE klass, VALUE digest, VALUE key, VALUE data) { unsigned char buf[EVP_MAX_MD_SIZE]; unsigned int buf_len; VALUE ret; StringValue(key); StringValue(data); if (!HMAC(GetDigestPtr(digest), RSTRING_PTR(key), RSTRING_LENINT(key), (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data), buf, &buf_len)) ossl_raise(eHMACError, "HMAC"); ret = rb_str_new(NULL, buf_len * 2); ossl_bin2hex(buf, RSTRING_PTR(ret), buf_len); return ret; } /* * INIT */ void Init_ossl_hmac(void) { #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif /* * Document-class: OpenSSL::HMAC * * OpenSSL::HMAC allows computing Hash-based Message Authentication Code * (HMAC). It is a type of message authentication code (MAC) involving a * hash function in combination with a key. HMAC can be used to verify the * integrity of a message as well as the authenticity. * * OpenSSL::HMAC has a similar interface to OpenSSL::Digest. * * === HMAC-SHA256 using one-shot interface * * key = "key" * data = "message-to-be-authenticated" * mac = OpenSSL::HMAC.hexdigest("SHA256", key, data) * #=> "cddb0db23f469c8bf072b21fd837149bd6ace9ab771cceef14c9e517cc93282e" * * === HMAC-SHA256 using incremental interface * * data1 = File.read("file1") * data2 = File.read("file2") * key = "key" * digest = OpenSSL::Digest::SHA256.new * hmac = OpenSSL::HMAC.new(key, digest) * hmac << data1 * hmac << data2 * mac = hmac.digest */ eHMACError = rb_define_class_under(mOSSL, "HMACError", eOSSLError); cHMAC = rb_define_class_under(mOSSL, "HMAC", rb_cObject); rb_define_alloc_func(cHMAC, ossl_hmac_alloc); rb_define_singleton_method(cHMAC, "digest", ossl_hmac_s_digest, 3); rb_define_singleton_method(cHMAC, "hexdigest", ossl_hmac_s_hexdigest, 3); rb_define_method(cHMAC, "initialize", ossl_hmac_initialize, 2); rb_define_copy_func(cHMAC, ossl_hmac_copy); rb_define_method(cHMAC, "reset", ossl_hmac_reset, 0); rb_define_method(cHMAC, "update", ossl_hmac_update, 1); rb_define_alias(cHMAC, "<<", "update"); rb_define_method(cHMAC, "digest", ossl_hmac_digest, 0); rb_define_method(cHMAC, "hexdigest", ossl_hmac_hexdigest, 0); rb_define_alias(cHMAC, "inspect", "hexdigest"); rb_define_alias(cHMAC, "to_s", "hexdigest"); } #else /* NO_HMAC */ # warning >>> OpenSSL is compiled without HMAC support <<< void Init_ossl_hmac(void) { rb_warning("HMAC is not available: OpenSSL is compiled without HMAC."); } #endif /* NO_HMAC */ openssl-2.0.9/ext/openssl/ossl_hmac.h000066400000000000000000000005631336157045000176210ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_HMAC_H_) #define _OSSL_HMAC_H_ extern VALUE cHMAC; extern VALUE eHMACError; void Init_ossl_hmac(void); #endif /* _OSSL_HMAC_H_ */ openssl-2.0.9/ext/openssl/ossl_ns_spki.c000066400000000000000000000231551336157045000203540ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define NewSPKI(klass) \ TypedData_Wrap_Struct((klass), &ossl_netscape_spki_type, 0) #define SetSPKI(obj, spki) do { \ if (!(spki)) { \ ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (spki); \ } while (0) #define GetSPKI(obj, spki) do { \ TypedData_Get_Struct((obj), NETSCAPE_SPKI, &ossl_netscape_spki_type, (spki)); \ if (!(spki)) { \ ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \ } \ } while (0) /* * Classes */ VALUE mNetscape; VALUE cSPKI; VALUE eSPKIError; /* * Public functions */ /* * Private functions */ static void ossl_netscape_spki_free(void *spki) { NETSCAPE_SPKI_free(spki); } static const rb_data_type_t ossl_netscape_spki_type = { "OpenSSL/NETSCAPE_SPKI", { 0, ossl_netscape_spki_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static VALUE ossl_spki_alloc(VALUE klass) { NETSCAPE_SPKI *spki; VALUE obj; obj = NewSPKI(klass); if (!(spki = NETSCAPE_SPKI_new())) { ossl_raise(eSPKIError, NULL); } SetSPKI(obj, spki); return obj; } /* * call-seq: * SPKI.new([request]) => spki * * === Parameters * * +request+ - optional raw request, either in PEM or DER format. */ static VALUE ossl_spki_initialize(int argc, VALUE *argv, VALUE self) { NETSCAPE_SPKI *spki; VALUE buffer; const unsigned char *p; if (rb_scan_args(argc, argv, "01", &buffer) == 0) { return self; } StringValue(buffer); if (!(spki = NETSCAPE_SPKI_b64_decode(RSTRING_PTR(buffer), RSTRING_LENINT(buffer)))) { ossl_clear_error(); p = (unsigned char *)RSTRING_PTR(buffer); if (!(spki = d2i_NETSCAPE_SPKI(NULL, &p, RSTRING_LEN(buffer)))) { ossl_raise(eSPKIError, NULL); } } NETSCAPE_SPKI_free(DATA_PTR(self)); SetSPKI(self, spki); return self; } /* * call-seq: * spki.to_der => DER-encoded string * * Returns the DER encoding of this SPKI. */ static VALUE ossl_spki_to_der(VALUE self) { NETSCAPE_SPKI *spki; VALUE str; long len; unsigned char *p; GetSPKI(self, spki); if ((len = i2d_NETSCAPE_SPKI(spki, NULL)) <= 0) ossl_raise(eX509CertError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_NETSCAPE_SPKI(spki, &p) <= 0) ossl_raise(eX509CertError, NULL); ossl_str_adjust(str, p); return str; } /* * call-seq: * spki.to_pem => PEM-encoded string * * Returns the PEM encoding of this SPKI. */ static VALUE ossl_spki_to_pem(VALUE self) { NETSCAPE_SPKI *spki; char *data; VALUE str; GetSPKI(self, spki); if (!(data = NETSCAPE_SPKI_b64_encode(spki))) { ossl_raise(eSPKIError, NULL); } str = ossl_buf2str(data, rb_long2int(strlen(data))); return str; } /* * call-seq: * spki.to_text => string * * Returns a textual representation of this SPKI, useful for debugging * purposes. */ static VALUE ossl_spki_print(VALUE self) { NETSCAPE_SPKI *spki; BIO *out; GetSPKI(self, spki); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eSPKIError, NULL); } if (!NETSCAPE_SPKI_print(out, spki)) { BIO_free(out); ossl_raise(eSPKIError, NULL); } return ossl_membio2str(out); } /* * call-seq: * spki.public_key => pkey * * Returns the public key associated with the SPKI, an instance of * OpenSSL::PKey. */ static VALUE ossl_spki_get_public_key(VALUE self) { NETSCAPE_SPKI *spki; EVP_PKEY *pkey; GetSPKI(self, spki); if (!(pkey = NETSCAPE_SPKI_get_pubkey(spki))) { /* adds an reference */ ossl_raise(eSPKIError, NULL); } return ossl_pkey_new(pkey); /* NO DUP - OK */ } /* * call-seq: * spki.public_key = pub => pkey * * === Parameters * * +pub+ - the public key to be set for this instance * * Sets the public key to be associated with the SPKI, an instance of * OpenSSL::PKey. This should be the public key corresponding to the * private key used for signing the SPKI. */ static VALUE ossl_spki_set_public_key(VALUE self, VALUE key) { NETSCAPE_SPKI *spki; EVP_PKEY *pkey; GetSPKI(self, spki); pkey = GetPKeyPtr(key); ossl_pkey_check_public_key(pkey); if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) ossl_raise(eSPKIError, "NETSCAPE_SPKI_set_pubkey"); return key; } /* * call-seq: * spki.challenge => string * * Returns the challenge string associated with this SPKI. */ static VALUE ossl_spki_get_challenge(VALUE self) { NETSCAPE_SPKI *spki; GetSPKI(self, spki); if (spki->spkac->challenge->length <= 0) { OSSL_Debug("Challenge.length <= 0?"); return rb_str_new(0, 0); } return rb_str_new((const char *)spki->spkac->challenge->data, spki->spkac->challenge->length); } /* * call-seq: * spki.challenge = str => string * * === Parameters * * +str+ - the challenge string to be set for this instance * * Sets the challenge to be associated with the SPKI. May be used by the * server, e.g. to prevent replay. */ static VALUE ossl_spki_set_challenge(VALUE self, VALUE str) { NETSCAPE_SPKI *spki; StringValue(str); GetSPKI(self, spki); if (!ASN1_STRING_set(spki->spkac->challenge, RSTRING_PTR(str), RSTRING_LENINT(str))) { ossl_raise(eSPKIError, NULL); } return str; } /* * call-seq: * spki.sign(key, digest) => spki * * === Parameters * * +key+ - the private key to be used for signing this instance * * +digest+ - the digest to be used for signing this instance * * To sign an SPKI, the private key corresponding to the public key set * for this instance should be used, in addition to a digest algorithm in * the form of an OpenSSL::Digest. The private key should be an instance of * OpenSSL::PKey. */ static VALUE ossl_spki_sign(VALUE self, VALUE key, VALUE digest) { NETSCAPE_SPKI *spki; EVP_PKEY *pkey; const EVP_MD *md; pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ md = GetDigestPtr(digest); GetSPKI(self, spki); if (!NETSCAPE_SPKI_sign(spki, pkey, md)) { ossl_raise(eSPKIError, NULL); } return self; } /* * call-seq: * spki.verify(key) => boolean * * === Parameters * * +key+ - the public key to be used for verifying the SPKI signature * * Returns +true+ if the signature is valid, +false+ otherwise. To verify an * SPKI, the public key contained within the SPKI should be used. */ static VALUE ossl_spki_verify(VALUE self, VALUE key) { NETSCAPE_SPKI *spki; EVP_PKEY *pkey; GetSPKI(self, spki); pkey = GetPKeyPtr(key); ossl_pkey_check_public_key(pkey); switch (NETSCAPE_SPKI_verify(spki, pkey)) { case 0: ossl_clear_error(); return Qfalse; case 1: return Qtrue; default: ossl_raise(eSPKIError, "NETSCAPE_SPKI_verify"); } } /* Document-class: OpenSSL::Netscape::SPKI * * A Simple Public Key Infrastructure implementation (pronounced "spooky"). * The structure is defined as * PublicKeyAndChallenge ::= SEQUENCE { * spki SubjectPublicKeyInfo, * challenge IA5STRING * } * * SignedPublicKeyAndChallenge ::= SEQUENCE { * publicKeyAndChallenge PublicKeyAndChallenge, * signatureAlgorithm AlgorithmIdentifier, * signature BIT STRING * } * where the definitions of SubjectPublicKeyInfo and AlgorithmIdentifier can * be found in RFC5280. SPKI is typically used in browsers for generating * a public/private key pair and a subsequent certificate request, using * the HTML element. * * == Examples * * === Creating an SPKI * key = OpenSSL::PKey::RSA.new 2048 * spki = OpenSSL::Netscape::SPKI.new * spki.challenge = "RandomChallenge" * spki.public_key = key.public_key * spki.sign(key, OpenSSL::Digest::SHA256.new) * #send a request containing this to a server generating a certificate * === Verifying an SPKI request * request = #... * spki = OpenSSL::Netscape::SPKI.new request * unless spki.verify(spki.public_key) * # signature is invalid * end * #proceed */ /* Document-module: OpenSSL::Netscape * * OpenSSL::Netscape is a namespace for SPKI (Simple Public Key * Infrastructure) which implements Signed Public Key and Challenge. * See {RFC 2692}[http://tools.ietf.org/html/rfc2692] and {RFC * 2693}[http://tools.ietf.org/html/rfc2692] for details. */ /* Document-class: OpenSSL::Netscape::SPKIError * * Generic Exception class that is raised if an error occurs during an * operation on an instance of OpenSSL::Netscape::SPKI. */ void Init_ossl_ns_spki(void) { #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif mNetscape = rb_define_module_under(mOSSL, "Netscape"); eSPKIError = rb_define_class_under(mNetscape, "SPKIError", eOSSLError); cSPKI = rb_define_class_under(mNetscape, "SPKI", rb_cObject); rb_define_alloc_func(cSPKI, ossl_spki_alloc); rb_define_method(cSPKI, "initialize", ossl_spki_initialize, -1); rb_define_method(cSPKI, "to_der", ossl_spki_to_der, 0); rb_define_method(cSPKI, "to_pem", ossl_spki_to_pem, 0); rb_define_alias(cSPKI, "to_s", "to_pem"); rb_define_method(cSPKI, "to_text", ossl_spki_print, 0); rb_define_method(cSPKI, "public_key", ossl_spki_get_public_key, 0); rb_define_method(cSPKI, "public_key=", ossl_spki_set_public_key, 1); rb_define_method(cSPKI, "sign", ossl_spki_sign, 2); rb_define_method(cSPKI, "verify", ossl_spki_verify, 1); rb_define_method(cSPKI, "challenge", ossl_spki_get_challenge, 0); rb_define_method(cSPKI, "challenge=", ossl_spki_set_challenge, 1); } openssl-2.0.9/ext/openssl/ossl_ns_spki.h000066400000000000000000000006271336157045000203600ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_NS_SPKI_H_) #define _OSSL_NS_SPKI_H_ extern VALUE mNetscape; extern VALUE cSPKI; extern VALUE eSPKIError; void Init_ossl_ns_spki(void); #endif /* _OSSL_NS_SPKI_H_ */ openssl-2.0.9/ext/openssl/ossl_ocsp.c000066400000000000000000001547041336157045000176570ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2003 Michal Rokos * Copyright (C) 2003 GOTOU Yuuzou * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #if !defined(OPENSSL_NO_OCSP) #define NewOCSPReq(klass) \ TypedData_Wrap_Struct((klass), &ossl_ocsp_request_type, 0) #define SetOCSPReq(obj, req) do { \ if(!(req)) ossl_raise(rb_eRuntimeError, "Request wasn't initialized!"); \ RTYPEDDATA_DATA(obj) = (req); \ } while (0) #define GetOCSPReq(obj, req) do { \ TypedData_Get_Struct((obj), OCSP_REQUEST, &ossl_ocsp_request_type, (req)); \ if(!(req)) ossl_raise(rb_eRuntimeError, "Request wasn't initialized!"); \ } while (0) #define SafeGetOCSPReq(obj, req) do { \ OSSL_Check_Kind((obj), cOCSPReq); \ GetOCSPReq((obj), (req)); \ } while (0) #define NewOCSPRes(klass) \ TypedData_Wrap_Struct((klass), &ossl_ocsp_response_type, 0) #define SetOCSPRes(obj, res) do { \ if(!(res)) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ RTYPEDDATA_DATA(obj) = (res); \ } while (0) #define GetOCSPRes(obj, res) do { \ TypedData_Get_Struct((obj), OCSP_RESPONSE, &ossl_ocsp_response_type, (res)); \ if(!(res)) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ } while (0) #define SafeGetOCSPRes(obj, res) do { \ OSSL_Check_Kind((obj), cOCSPRes); \ GetOCSPRes((obj), (res)); \ } while (0) #define NewOCSPBasicRes(klass) \ TypedData_Wrap_Struct((klass), &ossl_ocsp_basicresp_type, 0) #define SetOCSPBasicRes(obj, res) do { \ if(!(res)) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ RTYPEDDATA_DATA(obj) = (res); \ } while (0) #define GetOCSPBasicRes(obj, res) do { \ TypedData_Get_Struct((obj), OCSP_BASICRESP, &ossl_ocsp_basicresp_type, (res)); \ if(!(res)) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \ } while (0) #define SafeGetOCSPBasicRes(obj, res) do { \ OSSL_Check_Kind((obj), cOCSPBasicRes); \ GetOCSPBasicRes((obj), (res)); \ } while (0) #define NewOCSPSingleRes(klass) \ TypedData_Wrap_Struct((klass), &ossl_ocsp_singleresp_type, 0) #define SetOCSPSingleRes(obj, res) do { \ if(!(res)) ossl_raise(rb_eRuntimeError, "SingleResponse wasn't initialized!"); \ RTYPEDDATA_DATA(obj) = (res); \ } while (0) #define GetOCSPSingleRes(obj, res) do { \ TypedData_Get_Struct((obj), OCSP_SINGLERESP, &ossl_ocsp_singleresp_type, (res)); \ if(!(res)) ossl_raise(rb_eRuntimeError, "SingleResponse wasn't initialized!"); \ } while (0) #define SafeGetOCSPSingleRes(obj, res) do { \ OSSL_Check_Kind((obj), cOCSPSingleRes); \ GetOCSPSingleRes((obj), (res)); \ } while (0) #define NewOCSPCertId(klass) \ TypedData_Wrap_Struct((klass), &ossl_ocsp_certid_type, 0) #define SetOCSPCertId(obj, cid) do { \ if(!(cid)) ossl_raise(rb_eRuntimeError, "Cert ID wasn't initialized!"); \ RTYPEDDATA_DATA(obj) = (cid); \ } while (0) #define GetOCSPCertId(obj, cid) do { \ TypedData_Get_Struct((obj), OCSP_CERTID, &ossl_ocsp_certid_type, (cid)); \ if(!(cid)) ossl_raise(rb_eRuntimeError, "Cert ID wasn't initialized!"); \ } while (0) #define SafeGetOCSPCertId(obj, cid) do { \ OSSL_Check_Kind((obj), cOCSPCertId); \ GetOCSPCertId((obj), (cid)); \ } while (0) VALUE mOCSP; VALUE eOCSPError; VALUE cOCSPReq; VALUE cOCSPRes; VALUE cOCSPBasicRes; VALUE cOCSPSingleRes; VALUE cOCSPCertId; static void ossl_ocsp_request_free(void *ptr) { OCSP_REQUEST_free(ptr); } static const rb_data_type_t ossl_ocsp_request_type = { "OpenSSL/OCSP/REQUEST", { 0, ossl_ocsp_request_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static void ossl_ocsp_response_free(void *ptr) { OCSP_RESPONSE_free(ptr); } static const rb_data_type_t ossl_ocsp_response_type = { "OpenSSL/OCSP/RESPONSE", { 0, ossl_ocsp_response_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static void ossl_ocsp_basicresp_free(void *ptr) { OCSP_BASICRESP_free(ptr); } static const rb_data_type_t ossl_ocsp_basicresp_type = { "OpenSSL/OCSP/BASICRESP", { 0, ossl_ocsp_basicresp_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static void ossl_ocsp_singleresp_free(void *ptr) { OCSP_SINGLERESP_free(ptr); } static const rb_data_type_t ossl_ocsp_singleresp_type = { "OpenSSL/OCSP/SINGLERESP", { 0, ossl_ocsp_singleresp_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static void ossl_ocsp_certid_free(void *ptr) { OCSP_CERTID_free(ptr); } static const rb_data_type_t ossl_ocsp_certid_type = { "OpenSSL/OCSP/CERTID", { 0, ossl_ocsp_certid_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * Public */ static VALUE ossl_ocspcertid_new(OCSP_CERTID *cid) { VALUE obj = NewOCSPCertId(cOCSPCertId); SetOCSPCertId(obj, cid); return obj; } /* * OCSP::Resquest */ static VALUE ossl_ocspreq_alloc(VALUE klass) { OCSP_REQUEST *req; VALUE obj; obj = NewOCSPReq(klass); if (!(req = OCSP_REQUEST_new())) ossl_raise(eOCSPError, NULL); SetOCSPReq(obj, req); return obj; } static VALUE ossl_ocspreq_initialize_copy(VALUE self, VALUE other) { OCSP_REQUEST *req, *req_old, *req_new; rb_check_frozen(self); GetOCSPReq(self, req_old); SafeGetOCSPReq(other, req); req_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_REQUEST), req); if (!req_new) ossl_raise(eOCSPError, "ASN1_item_dup"); SetOCSPReq(self, req_new); OCSP_REQUEST_free(req_old); return self; } /* * call-seq: * OpenSSL::OCSP::Request.new -> request * OpenSSL::OCSP::Request.new(request_der) -> request * * Creates a new OpenSSL::OCSP::Request. The request may be created empty or * from a +request_der+ string. */ static VALUE ossl_ocspreq_initialize(int argc, VALUE *argv, VALUE self) { VALUE arg; OCSP_REQUEST *req, *req_new; const unsigned char *p; rb_scan_args(argc, argv, "01", &arg); if(!NIL_P(arg)){ GetOCSPReq(self, req); arg = ossl_to_der_if_possible(arg); StringValue(arg); p = (unsigned char *)RSTRING_PTR(arg); req_new = d2i_OCSP_REQUEST(NULL, &p, RSTRING_LEN(arg)); if (!req_new) ossl_raise(eOCSPError, "d2i_OCSP_REQUEST"); SetOCSPReq(self, req_new); OCSP_REQUEST_free(req); } return self; } /* * call-seq: * request.add_nonce(nonce = nil) -> request * * Adds a +nonce+ to the OCSP request. If no nonce is given a random one will * be generated. * * The nonce is used to prevent replay attacks but some servers do not support * it. */ static VALUE ossl_ocspreq_add_nonce(int argc, VALUE *argv, VALUE self) { OCSP_REQUEST *req; VALUE val; int ret; rb_scan_args(argc, argv, "01", &val); if(NIL_P(val)) { GetOCSPReq(self, req); ret = OCSP_request_add1_nonce(req, NULL, -1); } else{ StringValue(val); GetOCSPReq(self, req); ret = OCSP_request_add1_nonce(req, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val)); } if(!ret) ossl_raise(eOCSPError, NULL); return self; } /* * call-seq: * request.check_nonce(response) -> result * * Checks the nonce validity for this request and +response+. * * The return value is one of the following: * * -1 :: nonce in request only. * 0 :: nonces both present and not equal. * 1 :: nonces present and equal. * 2 :: nonces both absent. * 3 :: nonce present in response only. * * For most responses, clients can check +result+ > 0. If a responder doesn't * handle nonces result.nonzero? may be necessary. A result of * 0 is always an error. */ static VALUE ossl_ocspreq_check_nonce(VALUE self, VALUE basic_resp) { OCSP_REQUEST *req; OCSP_BASICRESP *bs; int res; GetOCSPReq(self, req); SafeGetOCSPBasicRes(basic_resp, bs); res = OCSP_check_nonce(req, bs); return INT2NUM(res); } /* * call-seq: * request.add_certid(certificate_id) -> request * * Adds +certificate_id+ to the request. */ static VALUE ossl_ocspreq_add_certid(VALUE self, VALUE certid) { OCSP_REQUEST *req; OCSP_CERTID *id, *id_new; GetOCSPReq(self, req); GetOCSPCertId(certid, id); if (!(id_new = OCSP_CERTID_dup(id))) ossl_raise(eOCSPError, "OCSP_CERTID_dup"); if (!OCSP_request_add0_id(req, id_new)) { OCSP_CERTID_free(id_new); ossl_raise(eOCSPError, "OCSP_request_add0_id"); } return self; } /* * call-seq: * request.certid -> [certificate_id, ...] * * Returns all certificate IDs in this request. */ static VALUE ossl_ocspreq_get_certid(VALUE self) { OCSP_REQUEST *req; OCSP_ONEREQ *one; OCSP_CERTID *id; VALUE ary, tmp; int i, count; GetOCSPReq(self, req); count = OCSP_request_onereq_count(req); ary = (count > 0) ? rb_ary_new() : Qnil; for(i = 0; i < count; i++){ one = OCSP_request_onereq_get0(req, i); tmp = NewOCSPCertId(cOCSPCertId); if(!(id = OCSP_CERTID_dup(OCSP_onereq_get0_id(one)))) ossl_raise(eOCSPError, NULL); SetOCSPCertId(tmp, id); rb_ary_push(ary, tmp); } return ary; } /* * call-seq: * request.sign(cert, key, certs = nil, flags = 0, digest = nil) -> self * * Signs this OCSP request using +cert+, +key+ and optional +digest+. If * +digest+ is not specified, SHA-1 is used. +certs+ is an optional Array of * additional certificates which are included in the request in addition to * the signer certificate. Note that if +certs+ is nil or not given, flag * OpenSSL::OCSP::NOCERTS is enabled. Pass an empty array to include only the * signer certificate. * * +flags+ can be a bitwise OR of the following constants: * * OpenSSL::OCSP::NOCERTS:: * Don't include any certificates in the request. +certs+ will be ignored. */ static VALUE ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self) { VALUE signer_cert, signer_key, certs, flags, digest; OCSP_REQUEST *req; X509 *signer; EVP_PKEY *key; STACK_OF(X509) *x509s = NULL; unsigned long flg = 0; const EVP_MD *md; int ret; rb_scan_args(argc, argv, "23", &signer_cert, &signer_key, &certs, &flags, &digest); GetOCSPReq(self, req); signer = GetX509CertPtr(signer_cert); key = GetPrivPKeyPtr(signer_key); if (!NIL_P(flags)) flg = NUM2INT(flags); if (NIL_P(digest)) md = EVP_sha1(); else md = GetDigestPtr(digest); if (NIL_P(certs)) flg |= OCSP_NOCERTS; else x509s = ossl_x509_ary2sk(certs); ret = OCSP_request_sign(req, signer, key, md, x509s, flg); sk_X509_pop_free(x509s, X509_free); if (!ret) ossl_raise(eOCSPError, NULL); return self; } /* * call-seq: * request.verify(certificates, store, flags = 0) -> true or false * * Verifies this request using the given +certificates+ and +store+. * +certificates+ is an array of OpenSSL::X509::Certificate, +store+ is an * OpenSSL::X509::Store. */ static VALUE ossl_ocspreq_verify(int argc, VALUE *argv, VALUE self) { VALUE certs, store, flags; OCSP_REQUEST *req; STACK_OF(X509) *x509s; X509_STORE *x509st; int flg, result; rb_scan_args(argc, argv, "21", &certs, &store, &flags); GetOCSPReq(self, req); x509st = GetX509StorePtr(store); flg = NIL_P(flags) ? 0 : NUM2INT(flags); x509s = ossl_x509_ary2sk(certs); result = OCSP_request_verify(req, x509s, x509st, flg); sk_X509_pop_free(x509s, X509_free); if (result <= 0) ossl_clear_error(); return result > 0 ? Qtrue : Qfalse; } /* * Returns this request as a DER-encoded string */ static VALUE ossl_ocspreq_to_der(VALUE self) { OCSP_REQUEST *req; VALUE str; unsigned char *p; long len; GetOCSPReq(self, req); if((len = i2d_OCSP_REQUEST(req, NULL)) <= 0) ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_OCSP_REQUEST(req, &p) <= 0) ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; } /* * OCSP::Response */ /* call-seq: * OpenSSL::OCSP::Response.create(status, basic_response = nil) -> response * * Creates an OpenSSL::OCSP::Response from +status+ and +basic_response+. */ static VALUE ossl_ocspres_s_create(VALUE klass, VALUE status, VALUE basic_resp) { OCSP_BASICRESP *bs; OCSP_RESPONSE *res; VALUE obj; int st = NUM2INT(status); if(NIL_P(basic_resp)) bs = NULL; else GetOCSPBasicRes(basic_resp, bs); /* NO NEED TO DUP */ obj = NewOCSPRes(klass); if(!(res = OCSP_response_create(st, bs))) ossl_raise(eOCSPError, NULL); SetOCSPRes(obj, res); return obj; } static VALUE ossl_ocspres_alloc(VALUE klass) { OCSP_RESPONSE *res; VALUE obj; obj = NewOCSPRes(klass); if(!(res = OCSP_RESPONSE_new())) ossl_raise(eOCSPError, NULL); SetOCSPRes(obj, res); return obj; } static VALUE ossl_ocspres_initialize_copy(VALUE self, VALUE other) { OCSP_RESPONSE *res, *res_old, *res_new; rb_check_frozen(self); GetOCSPRes(self, res_old); SafeGetOCSPRes(other, res); res_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_RESPONSE), res); if (!res_new) ossl_raise(eOCSPError, "ASN1_item_dup"); SetOCSPRes(self, res_new); OCSP_RESPONSE_free(res_old); return self; } /* * call-seq: * OpenSSL::OCSP::Response.new -> response * OpenSSL::OCSP::Response.new(response_der) -> response * * Creates a new OpenSSL::OCSP::Response. The response may be created empty or * from a +response_der+ string. */ static VALUE ossl_ocspres_initialize(int argc, VALUE *argv, VALUE self) { VALUE arg; OCSP_RESPONSE *res, *res_new; const unsigned char *p; rb_scan_args(argc, argv, "01", &arg); if(!NIL_P(arg)){ GetOCSPRes(self, res); arg = ossl_to_der_if_possible(arg); StringValue(arg); p = (unsigned char *)RSTRING_PTR(arg); res_new = d2i_OCSP_RESPONSE(NULL, &p, RSTRING_LEN(arg)); if (!res_new) ossl_raise(eOCSPError, "d2i_OCSP_RESPONSE"); SetOCSPRes(self, res_new); OCSP_RESPONSE_free(res); } return self; } /* * call-seq: * response.status -> Integer * * Returns the status of the response. */ static VALUE ossl_ocspres_status(VALUE self) { OCSP_RESPONSE *res; int st; GetOCSPRes(self, res); st = OCSP_response_status(res); return INT2NUM(st); } /* * call-seq: * response.status_string -> String * * Returns a status string for the response. */ static VALUE ossl_ocspres_status_string(VALUE self) { OCSP_RESPONSE *res; int st; GetOCSPRes(self, res); st = OCSP_response_status(res); return rb_str_new2(OCSP_response_status_str(st)); } /* * call-seq: * response.basic * * Returns a BasicResponse for this response */ static VALUE ossl_ocspres_get_basic(VALUE self) { OCSP_RESPONSE *res; OCSP_BASICRESP *bs; VALUE ret; GetOCSPRes(self, res); ret = NewOCSPBasicRes(cOCSPBasicRes); if(!(bs = OCSP_response_get1_basic(res))) return Qnil; SetOCSPBasicRes(ret, bs); return ret; } /* * call-seq: * response.to_der -> String * * Returns this response as a DER-encoded string. */ static VALUE ossl_ocspres_to_der(VALUE self) { OCSP_RESPONSE *res; VALUE str; long len; unsigned char *p; GetOCSPRes(self, res); if((len = i2d_OCSP_RESPONSE(res, NULL)) <= 0) ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_OCSP_RESPONSE(res, &p) <= 0) ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; } /* * OCSP::BasicResponse */ static VALUE ossl_ocspbres_alloc(VALUE klass) { OCSP_BASICRESP *bs; VALUE obj; obj = NewOCSPBasicRes(klass); if(!(bs = OCSP_BASICRESP_new())) ossl_raise(eOCSPError, NULL); SetOCSPBasicRes(obj, bs); return obj; } static VALUE ossl_ocspbres_initialize_copy(VALUE self, VALUE other) { OCSP_BASICRESP *bs, *bs_old, *bs_new; rb_check_frozen(self); GetOCSPBasicRes(self, bs_old); SafeGetOCSPBasicRes(other, bs); bs_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_BASICRESP), bs); if (!bs_new) ossl_raise(eOCSPError, "ASN1_item_dup"); SetOCSPBasicRes(self, bs_new); OCSP_BASICRESP_free(bs_old); return self; } /* * call-seq: * OpenSSL::OCSP::BasicResponse.new(der_string = nil) -> basic_response * * Creates a new BasicResponse. If +der_string+ is given, decodes +der_string+ * as DER. */ static VALUE ossl_ocspbres_initialize(int argc, VALUE *argv, VALUE self) { VALUE arg; OCSP_BASICRESP *res, *res_new; const unsigned char *p; rb_scan_args(argc, argv, "01", &arg); if (!NIL_P(arg)) { GetOCSPBasicRes(self, res); arg = ossl_to_der_if_possible(arg); StringValue(arg); p = (unsigned char *)RSTRING_PTR(arg); res_new = d2i_OCSP_BASICRESP(NULL, &p, RSTRING_LEN(arg)); if (!res_new) ossl_raise(eOCSPError, "d2i_OCSP_BASICRESP"); SetOCSPBasicRes(self, res_new); OCSP_BASICRESP_free(res); } return self; } /* * call-seq: * basic_response.copy_nonce(request) -> Integer * * Copies the nonce from +request+ into this response. Returns 1 on success * and 0 on failure. */ static VALUE ossl_ocspbres_copy_nonce(VALUE self, VALUE request) { OCSP_BASICRESP *bs; OCSP_REQUEST *req; int ret; GetOCSPBasicRes(self, bs); SafeGetOCSPReq(request, req); ret = OCSP_copy_nonce(bs, req); return INT2NUM(ret); } /* * call-seq: * basic_response.add_nonce(nonce = nil) * * Adds +nonce+ to this response. If no nonce was provided a random nonce * will be added. */ static VALUE ossl_ocspbres_add_nonce(int argc, VALUE *argv, VALUE self) { OCSP_BASICRESP *bs; VALUE val; int ret; rb_scan_args(argc, argv, "01", &val); if(NIL_P(val)) { GetOCSPBasicRes(self, bs); ret = OCSP_basic_add1_nonce(bs, NULL, -1); } else{ StringValue(val); GetOCSPBasicRes(self, bs); ret = OCSP_basic_add1_nonce(bs, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val)); } if(!ret) ossl_raise(eOCSPError, NULL); return self; } static VALUE add_status_convert_time(VALUE obj) { ASN1_TIME *time; if (RB_INTEGER_TYPE_P(obj)) time = X509_gmtime_adj(NULL, NUM2INT(obj)); else time = ossl_x509_time_adjust(NULL, obj); if (!time) ossl_raise(eOCSPError, NULL); return (VALUE)time; } /* * call-seq: * basic_response.add_status(certificate_id, status, reason, revocation_time, this_update, next_update, extensions) -> basic_response * * Adds a certificate status for +certificate_id+. +status+ is the status, and * must be one of these: * * - OpenSSL::OCSP::V_CERTSTATUS_GOOD * - OpenSSL::OCSP::V_CERTSTATUS_REVOKED * - OpenSSL::OCSP::V_CERTSTATUS_UNKNOWN * * +reason+ and +revocation_time+ can be given only when +status+ is * OpenSSL::OCSP::V_CERTSTATUS_REVOKED. +reason+ describes the reason for the * revocation, and must be one of OpenSSL::OCSP::REVOKED_STATUS_* constants. * +revocation_time+ is the time when the certificate is revoked. * * +this_update+ and +next_update+ indicate the time at which ths status is * verified to be correct and the time at or before which newer information * will be available, respectively. +next_update+ is optional. * * +extensions+ is an Array of OpenSSL::X509::Extension to be included in the * SingleResponse. This is also optional. * * Note that the times, +revocation_time+, +this_update+ and +next_update+ * can be specified in either of Integer or Time object. If they are Integer, it * is treated as the relative seconds from the current time. */ static VALUE ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status, VALUE reason, VALUE revtime, VALUE thisupd, VALUE nextupd, VALUE ext) { OCSP_BASICRESP *bs; OCSP_SINGLERESP *single; OCSP_CERTID *id; ASN1_TIME *ths = NULL, *nxt = NULL, *rev = NULL; int st, rsn = 0, error = 0, rstatus = 0; long i; VALUE tmp; GetOCSPBasicRes(self, bs); SafeGetOCSPCertId(cid, id); st = NUM2INT(status); if (!NIL_P(ext)) { /* All ext's members must be X509::Extension */ ext = rb_check_array_type(ext); for (i = 0; i < RARRAY_LEN(ext); i++) OSSL_Check_Kind(RARRAY_AREF(ext, i), cX509Ext); } if (st == V_OCSP_CERTSTATUS_REVOKED) { rsn = NUM2INT(reason); tmp = rb_protect(add_status_convert_time, revtime, &rstatus); if (rstatus) goto err; rev = (ASN1_TIME *)tmp; } tmp = rb_protect(add_status_convert_time, thisupd, &rstatus); if (rstatus) goto err; ths = (ASN1_TIME *)tmp; if (!NIL_P(nextupd)) { tmp = rb_protect(add_status_convert_time, nextupd, &rstatus); if (rstatus) goto err; nxt = (ASN1_TIME *)tmp; } if(!(single = OCSP_basic_add1_status(bs, id, st, rsn, rev, ths, nxt))){ error = 1; goto err; } if(!NIL_P(ext)){ X509_EXTENSION *x509ext; for(i = 0; i < RARRAY_LEN(ext); i++){ x509ext = GetX509ExtPtr(RARRAY_AREF(ext, i)); if(!OCSP_SINGLERESP_add_ext(single, x509ext, -1)){ error = 1; goto err; } } } err: ASN1_TIME_free(ths); ASN1_TIME_free(nxt); ASN1_TIME_free(rev); if(error) ossl_raise(eOCSPError, NULL); if(rstatus) rb_jump_tag(rstatus); return self; } /* * call-seq: * basic_response.status -> statuses * * Returns an Array of statuses for this response. Each status contains a * CertificateId, the status (0 for good, 1 for revoked, 2 for unknown), the * reason for the status, the revocation time, the time of this update, the time * for the next update and a list of OpenSSL::X509::Extensions. * * This should be superseded by BasicResponse#responses and #find_response that * return SingleResponse. */ static VALUE ossl_ocspbres_get_status(VALUE self) { OCSP_BASICRESP *bs; OCSP_SINGLERESP *single; OCSP_CERTID *cid; ASN1_TIME *revtime, *thisupd, *nextupd; int status, reason; X509_EXTENSION *x509ext; VALUE ret, ary, ext; int count, ext_count, i, j; GetOCSPBasicRes(self, bs); ret = rb_ary_new(); count = OCSP_resp_count(bs); for(i = 0; i < count; i++){ single = OCSP_resp_get0(bs, i); if(!single) continue; revtime = thisupd = nextupd = NULL; status = OCSP_single_get0_status(single, &reason, &revtime, &thisupd, &nextupd); if(status < 0) continue; if(!(cid = OCSP_CERTID_dup((OCSP_CERTID *)OCSP_SINGLERESP_get0_id(single)))) /* FIXME */ ossl_raise(eOCSPError, NULL); ary = rb_ary_new(); rb_ary_push(ary, ossl_ocspcertid_new(cid)); rb_ary_push(ary, INT2NUM(status)); rb_ary_push(ary, INT2NUM(reason)); rb_ary_push(ary, revtime ? asn1time_to_time(revtime) : Qnil); rb_ary_push(ary, thisupd ? asn1time_to_time(thisupd) : Qnil); rb_ary_push(ary, nextupd ? asn1time_to_time(nextupd) : Qnil); ext = rb_ary_new(); ext_count = OCSP_SINGLERESP_get_ext_count(single); for(j = 0; j < ext_count; j++){ x509ext = OCSP_SINGLERESP_get_ext(single, j); rb_ary_push(ext, ossl_x509ext_new(x509ext)); } rb_ary_push(ary, ext); rb_ary_push(ret, ary); } return ret; } static VALUE ossl_ocspsres_new(OCSP_SINGLERESP *); /* * call-seq: * basic_response.responses -> Array of SingleResponse * * Returns an Array of SingleResponse for this BasicResponse. */ static VALUE ossl_ocspbres_get_responses(VALUE self) { OCSP_BASICRESP *bs; VALUE ret; int count, i; GetOCSPBasicRes(self, bs); count = OCSP_resp_count(bs); ret = rb_ary_new2(count); for (i = 0; i < count; i++) { OCSP_SINGLERESP *sres, *sres_new; sres = OCSP_resp_get0(bs, i); sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres); if (!sres_new) ossl_raise(eOCSPError, "ASN1_item_dup"); rb_ary_push(ret, ossl_ocspsres_new(sres_new)); } return ret; } /* * call-seq: * basic_response.find_response(certificate_id) -> SingleResponse | nil * * Returns a SingleResponse whose CertId matches with +certificate_id+, or nil * if this BasicResponse does not contain it. */ static VALUE ossl_ocspbres_find_response(VALUE self, VALUE target) { OCSP_BASICRESP *bs; OCSP_SINGLERESP *sres, *sres_new; OCSP_CERTID *id; int n; SafeGetOCSPCertId(target, id); GetOCSPBasicRes(self, bs); if ((n = OCSP_resp_find(bs, id, -1)) == -1) return Qnil; sres = OCSP_resp_get0(bs, n); sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres); if (!sres_new) ossl_raise(eOCSPError, "ASN1_item_dup"); return ossl_ocspsres_new(sres_new); } /* * call-seq: * basic_response.sign(cert, key, certs = nil, flags = 0, digest = nil) -> self * * Signs this OCSP response using the +cert+, +key+ and optional +digest+. This * behaves in the similar way as OpenSSL::OCSP::Request#sign. * * +flags+ can include: * OpenSSL::OCSP::NOCERTS:: don't include certificates * OpenSSL::OCSP::NOTIME:: don't set producedAt * OpenSSL::OCSP::RESPID_KEY:: use signer's public key hash as responderID */ static VALUE ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self) { VALUE signer_cert, signer_key, certs, flags, digest; OCSP_BASICRESP *bs; X509 *signer; EVP_PKEY *key; STACK_OF(X509) *x509s = NULL; unsigned long flg = 0; const EVP_MD *md; int ret; rb_scan_args(argc, argv, "23", &signer_cert, &signer_key, &certs, &flags, &digest); GetOCSPBasicRes(self, bs); signer = GetX509CertPtr(signer_cert); key = GetPrivPKeyPtr(signer_key); if (!NIL_P(flags)) flg = NUM2INT(flags); if (NIL_P(digest)) md = EVP_sha1(); else md = GetDigestPtr(digest); if (NIL_P(certs)) flg |= OCSP_NOCERTS; else x509s = ossl_x509_ary2sk(certs); ret = OCSP_basic_sign(bs, signer, key, md, x509s, flg); sk_X509_pop_free(x509s, X509_free); if (!ret) ossl_raise(eOCSPError, NULL); return self; } /* * call-seq: * basic_response.verify(certificates, store, flags = 0) -> true or false * * Verifies the signature of the response using the given +certificates+ and * +store+. This works in the similar way as OpenSSL::OCSP::Request#verify. */ static VALUE ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self) { VALUE certs, store, flags; OCSP_BASICRESP *bs; STACK_OF(X509) *x509s; X509_STORE *x509st; int flg, result; rb_scan_args(argc, argv, "21", &certs, &store, &flags); GetOCSPBasicRes(self, bs); x509st = GetX509StorePtr(store); flg = NIL_P(flags) ? 0 : NUM2INT(flags); x509s = ossl_x509_ary2sk(certs); #if (OPENSSL_VERSION_NUMBER < 0x1000202fL) || defined(LIBRESSL_VERSION_NUMBER) /* * OpenSSL had a bug that it doesn't use the certificates in x509s for * verifying the chain. This can be a problem when the response is signed by * a certificate issued by an intermediate CA. * * root_ca * | * intermediate_ca * |-------------| * end_entity ocsp_signer * * When the certificate hierarchy is like this, and the response contains * only ocsp_signer certificate, the following code wrongly fails. * * store = OpenSSL::X509::Store.new; store.add_cert(root_ca) * basic_response.verify([intermediate_ca], store) * * So add the certificates in x509s to the embedded certificates list first. * * This is fixed in OpenSSL 0.9.8zg, 1.0.0s, 1.0.1n, 1.0.2b. But it still * exists in LibreSSL 2.1.10, 2.2.9, 2.3.6, 2.4.1. */ if (!(flg & (OCSP_NOCHAIN | OCSP_NOVERIFY)) && sk_X509_num(x509s) && sk_X509_num(bs->certs)) { int i; bs = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_BASICRESP), bs); if (!bs) { sk_X509_pop_free(x509s, X509_free); ossl_raise(eOCSPError, "ASN1_item_dup"); } for (i = 0; i < sk_X509_num(x509s); i++) { if (!OCSP_basic_add1_cert(bs, sk_X509_value(x509s, i))) { sk_X509_pop_free(x509s, X509_free); OCSP_BASICRESP_free(bs); ossl_raise(eOCSPError, "OCSP_basic_add1_cert"); } } result = OCSP_basic_verify(bs, x509s, x509st, flg); OCSP_BASICRESP_free(bs); } else { result = OCSP_basic_verify(bs, x509s, x509st, flg); } #else result = OCSP_basic_verify(bs, x509s, x509st, flg); #endif sk_X509_pop_free(x509s, X509_free); if (result <= 0) ossl_clear_error(); return result > 0 ? Qtrue : Qfalse; } /* * call-seq: * basic_response.to_der -> String * * Encodes this basic response into a DER-encoded string. */ static VALUE ossl_ocspbres_to_der(VALUE self) { OCSP_BASICRESP *res; VALUE str; long len; unsigned char *p; GetOCSPBasicRes(self, res); if ((len = i2d_OCSP_BASICRESP(res, NULL)) <= 0) ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_OCSP_BASICRESP(res, &p) <= 0) ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; } /* * OCSP::SingleResponse */ static VALUE ossl_ocspsres_new(OCSP_SINGLERESP *sres) { VALUE obj; obj = NewOCSPSingleRes(cOCSPSingleRes); SetOCSPSingleRes(obj, sres); return obj; } static VALUE ossl_ocspsres_alloc(VALUE klass) { OCSP_SINGLERESP *sres; VALUE obj; obj = NewOCSPSingleRes(klass); if (!(sres = OCSP_SINGLERESP_new())) ossl_raise(eOCSPError, NULL); SetOCSPSingleRes(obj, sres); return obj; } /* * call-seq: * OpenSSL::OCSP::SingleResponse.new(der_string) -> SingleResponse * * Creates a new SingleResponse from +der_string+. */ static VALUE ossl_ocspsres_initialize(VALUE self, VALUE arg) { OCSP_SINGLERESP *res, *res_new; const unsigned char *p; arg = ossl_to_der_if_possible(arg); StringValue(arg); GetOCSPSingleRes(self, res); p = (unsigned char*)RSTRING_PTR(arg); res_new = d2i_OCSP_SINGLERESP(NULL, &p, RSTRING_LEN(arg)); if (!res_new) ossl_raise(eOCSPError, "d2i_OCSP_SINGLERESP"); SetOCSPSingleRes(self, res_new); OCSP_SINGLERESP_free(res); return self; } static VALUE ossl_ocspsres_initialize_copy(VALUE self, VALUE other) { OCSP_SINGLERESP *sres, *sres_old, *sres_new; rb_check_frozen(self); GetOCSPSingleRes(self, sres_old); SafeGetOCSPSingleRes(other, sres); sres_new = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_SINGLERESP), sres); if (!sres_new) ossl_raise(eOCSPError, "ASN1_item_dup"); SetOCSPSingleRes(self, sres_new); OCSP_SINGLERESP_free(sres_old); return self; } /* * call-seq: * single_response.check_validity(nsec = 0, maxsec = -1) -> true | false * * Checks the validity of thisUpdate and nextUpdate fields of this * SingleResponse. This checks the current time is within the range thisUpdate * to nextUpdate. * * It is possible that the OCSP request takes a few seconds or the time is not * accurate. To avoid rejecting a valid response, this method allows the times * to be within +nsec+ of the current time. * * Some responders don't set the nextUpdate field. This may cause a very old * response to be considered valid. The +maxsec+ parameter can be used to limit * the age of responses. */ static VALUE ossl_ocspsres_check_validity(int argc, VALUE *argv, VALUE self) { OCSP_SINGLERESP *sres; ASN1_GENERALIZEDTIME *this_update, *next_update; VALUE nsec_v, maxsec_v; int nsec, maxsec, status, ret; rb_scan_args(argc, argv, "02", &nsec_v, &maxsec_v); nsec = NIL_P(nsec_v) ? 0 : NUM2INT(nsec_v); maxsec = NIL_P(maxsec_v) ? -1 : NUM2INT(maxsec_v); GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, NULL, &this_update, &next_update); if (status < 0) ossl_raise(eOCSPError, "OCSP_single_get0_status"); ret = OCSP_check_validity(this_update, next_update, nsec, maxsec); if (ret) return Qtrue; else { ossl_clear_error(); return Qfalse; } } /* * call-seq: * single_response.certid -> CertificateId * * Returns the CertificateId for which this SingleResponse is. */ static VALUE ossl_ocspsres_get_certid(VALUE self) { OCSP_SINGLERESP *sres; OCSP_CERTID *id; GetOCSPSingleRes(self, sres); id = OCSP_CERTID_dup((OCSP_CERTID *)OCSP_SINGLERESP_get0_id(sres)); /* FIXME */ return ossl_ocspcertid_new(id); } /* * call-seq: * single_response.cert_status -> Integer * * Returns the status of the certificate identified by the certid. * The return value may be one of these constant: * * - V_CERTSTATUS_GOOD * - V_CERTSTATUS_REVOKED * - V_CERTSTATUS_UNKNOWN * * When the status is V_CERTSTATUS_REVOKED, the time at which the certificate * was revoked can be retrieved by #revocation_time. */ static VALUE ossl_ocspsres_get_cert_status(VALUE self) { OCSP_SINGLERESP *sres; int status; GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, NULL, NULL, NULL); if (status < 0) ossl_raise(eOCSPError, "OCSP_single_get0_status"); return INT2NUM(status); } /* * call-seq: * single_response.this_update -> Time */ static VALUE ossl_ocspsres_get_this_update(VALUE self) { OCSP_SINGLERESP *sres; int status; ASN1_GENERALIZEDTIME *time; GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, NULL, &time, NULL); if (status < 0) ossl_raise(eOCSPError, "OCSP_single_get0_status"); return asn1time_to_time(time); /* will handle NULL */ } /* * call-seq: * single_response.next_update -> Time | nil */ static VALUE ossl_ocspsres_get_next_update(VALUE self) { OCSP_SINGLERESP *sres; int status; ASN1_GENERALIZEDTIME *time; GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, NULL, NULL, &time); if (status < 0) ossl_raise(eOCSPError, "OCSP_single_get0_status"); return asn1time_to_time(time); } /* * call-seq: * single_response.revocation_time -> Time | nil */ static VALUE ossl_ocspsres_get_revocation_time(VALUE self) { OCSP_SINGLERESP *sres; int status; ASN1_GENERALIZEDTIME *time; GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, NULL, &time, NULL, NULL); if (status < 0) ossl_raise(eOCSPError, "OCSP_single_get0_status"); if (status != V_OCSP_CERTSTATUS_REVOKED) ossl_raise(eOCSPError, "certificate is not revoked"); return asn1time_to_time(time); } /* * call-seq: * single_response.revocation_reason -> Integer | nil */ static VALUE ossl_ocspsres_get_revocation_reason(VALUE self) { OCSP_SINGLERESP *sres; int status, reason; GetOCSPSingleRes(self, sres); status = OCSP_single_get0_status(sres, &reason, NULL, NULL, NULL); if (status < 0) ossl_raise(eOCSPError, "OCSP_single_get0_status"); if (status != V_OCSP_CERTSTATUS_REVOKED) ossl_raise(eOCSPError, "certificate is not revoked"); return INT2NUM(reason); } /* * call-seq: * single_response.extensions -> Array of X509::Extension */ static VALUE ossl_ocspsres_get_extensions(VALUE self) { OCSP_SINGLERESP *sres; X509_EXTENSION *ext; int count, i; VALUE ary; GetOCSPSingleRes(self, sres); count = OCSP_SINGLERESP_get_ext_count(sres); ary = rb_ary_new2(count); for (i = 0; i < count; i++) { ext = OCSP_SINGLERESP_get_ext(sres, i); rb_ary_push(ary, ossl_x509ext_new(ext)); /* will dup */ } return ary; } /* * call-seq: * single_response.to_der -> String * * Encodes this SingleResponse into a DER-encoded string. */ static VALUE ossl_ocspsres_to_der(VALUE self) { OCSP_SINGLERESP *sres; VALUE str; long len; unsigned char *p; GetOCSPSingleRes(self, sres); if ((len = i2d_OCSP_SINGLERESP(sres, NULL)) <= 0) ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_OCSP_SINGLERESP(sres, &p) <= 0) ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; } /* * OCSP::CertificateId */ static VALUE ossl_ocspcid_alloc(VALUE klass) { OCSP_CERTID *id; VALUE obj; obj = NewOCSPCertId(klass); if(!(id = OCSP_CERTID_new())) ossl_raise(eOCSPError, NULL); SetOCSPCertId(obj, id); return obj; } static VALUE ossl_ocspcid_initialize_copy(VALUE self, VALUE other) { OCSP_CERTID *cid, *cid_old, *cid_new; rb_check_frozen(self); GetOCSPCertId(self, cid_old); SafeGetOCSPCertId(other, cid); cid_new = OCSP_CERTID_dup(cid); if (!cid_new) ossl_raise(eOCSPError, "OCSP_CERTID_dup"); SetOCSPCertId(self, cid_new); OCSP_CERTID_free(cid_old); return self; } /* * call-seq: * OpenSSL::OCSP::CertificateId.new(subject, issuer, digest = nil) -> certificate_id * OpenSSL::OCSP::CertificateId.new(der_string) -> certificate_id * * Creates a new OpenSSL::OCSP::CertificateId for the given +subject+ and * +issuer+ X509 certificates. The +digest+ is used to compute the * certificate ID and must be an OpenSSL::Digest instance. * * If only one argument is given, decodes it as DER representation of a * certificate ID. */ static VALUE ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self) { OCSP_CERTID *id, *newid; VALUE subject, issuer, digest; GetOCSPCertId(self, id); if (rb_scan_args(argc, argv, "12", &subject, &issuer, &digest) == 1) { VALUE arg; const unsigned char *p; arg = ossl_to_der_if_possible(subject); StringValue(arg); p = (unsigned char *)RSTRING_PTR(arg); newid = d2i_OCSP_CERTID(NULL, &p, RSTRING_LEN(arg)); if (!newid) ossl_raise(eOCSPError, "d2i_OCSP_CERTID"); } else { X509 *x509s, *x509i; const EVP_MD *md; x509s = GetX509CertPtr(subject); /* NO NEED TO DUP */ x509i = GetX509CertPtr(issuer); /* NO NEED TO DUP */ md = !NIL_P(digest) ? GetDigestPtr(digest) : NULL; newid = OCSP_cert_to_id(md, x509s, x509i); if (!newid) ossl_raise(eOCSPError, "OCSP_cert_to_id"); } SetOCSPCertId(self, newid); OCSP_CERTID_free(id); return self; } /* * call-seq: * certificate_id.cmp(other) -> true or false * * Compares this certificate id with +other+ and returns true if they are the * same. */ static VALUE ossl_ocspcid_cmp(VALUE self, VALUE other) { OCSP_CERTID *id, *id2; int result; GetOCSPCertId(self, id); SafeGetOCSPCertId(other, id2); result = OCSP_id_cmp(id, id2); return (result == 0) ? Qtrue : Qfalse; } /* * call-seq: * certificate_id.cmp_issuer(other) -> true or false * * Compares this certificate id's issuer with +other+ and returns true if * they are the same. */ static VALUE ossl_ocspcid_cmp_issuer(VALUE self, VALUE other) { OCSP_CERTID *id, *id2; int result; GetOCSPCertId(self, id); SafeGetOCSPCertId(other, id2); result = OCSP_id_issuer_cmp(id, id2); return (result == 0) ? Qtrue : Qfalse; } /* * call-seq: * certificate_id.serial -> Integer * * Returns the serial number of the certificate for which status is being * requested. */ static VALUE ossl_ocspcid_get_serial(VALUE self) { OCSP_CERTID *id; ASN1_INTEGER *serial; GetOCSPCertId(self, id); OCSP_id_get0_info(NULL, NULL, NULL, &serial, id); return asn1integer_to_num(serial); } /* * call-seq: * certificate_id.issuer_name_hash -> String * * Returns the issuerNameHash of this certificate ID, the hash of the * issuer's distinguished name calculated with the hashAlgorithm. */ static VALUE ossl_ocspcid_get_issuer_name_hash(VALUE self) { OCSP_CERTID *id; ASN1_OCTET_STRING *name_hash; VALUE ret; GetOCSPCertId(self, id); OCSP_id_get0_info(&name_hash, NULL, NULL, NULL, id); ret = rb_str_new(NULL, name_hash->length * 2); ossl_bin2hex(name_hash->data, RSTRING_PTR(ret), name_hash->length); return ret; } /* * call-seq: * certificate_id.issuer_key_hash -> String * * Returns the issuerKeyHash of this certificate ID, the hash of the issuer's * public key. */ static VALUE ossl_ocspcid_get_issuer_key_hash(VALUE self) { OCSP_CERTID *id; ASN1_OCTET_STRING *key_hash; VALUE ret; GetOCSPCertId(self, id); OCSP_id_get0_info(NULL, NULL, &key_hash, NULL, id); ret = rb_str_new(NULL, key_hash->length * 2); ossl_bin2hex(key_hash->data, RSTRING_PTR(ret), key_hash->length); return ret; } /* * call-seq: * certificate_id.hash_algorithm -> String * * Returns the ln (long name) of the hash algorithm used to generate * the issuerNameHash and the issuerKeyHash values. */ static VALUE ossl_ocspcid_get_hash_algorithm(VALUE self) { OCSP_CERTID *id; ASN1_OBJECT *oid; BIO *out; GetOCSPCertId(self, id); OCSP_id_get0_info(NULL, &oid, NULL, NULL, id); if (!(out = BIO_new(BIO_s_mem()))) ossl_raise(eOCSPError, "BIO_new"); if (!i2a_ASN1_OBJECT(out, oid)) { BIO_free(out); ossl_raise(eOCSPError, "i2a_ASN1_OBJECT"); } return ossl_membio2str(out); } /* * call-seq: * certificate_id.to_der -> String * * Encodes this certificate identifier into a DER-encoded string. */ static VALUE ossl_ocspcid_to_der(VALUE self) { OCSP_CERTID *id; VALUE str; long len; unsigned char *p; GetOCSPCertId(self, id); if ((len = i2d_OCSP_CERTID(id, NULL)) <= 0) ossl_raise(eOCSPError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_OCSP_CERTID(id, &p) <= 0) ossl_raise(eOCSPError, NULL); ossl_str_adjust(str, p); return str; } void Init_ossl_ocsp(void) { #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif /* * OpenSSL::OCSP implements Online Certificate Status Protocol requests * and responses. * * Creating and sending an OCSP request requires a subject certificate * that contains an OCSP URL in an authorityInfoAccess extension and the * issuer certificate for the subject certificate. First, load the issuer * and subject certificates: * * subject = OpenSSL::X509::Certificate.new subject_pem * issuer = OpenSSL::X509::Certificate.new issuer_pem * * To create the request we need to create a certificate ID for the * subject certificate so the CA knows which certificate we are asking * about: * * digest = OpenSSL::Digest::SHA1.new * certificate_id = * OpenSSL::OCSP::CertificateId.new subject, issuer, digest * * Then create a request and add the certificate ID to it: * * request = OpenSSL::OCSP::Request.new * request.add_certid certificate_id * * Adding a nonce to the request protects against replay attacks but not * all CA process the nonce. * * request.add_nonce * * To submit the request to the CA for verification we need to extract the * OCSP URI from the subject certificate: * * authority_info_access = subject.extensions.find do |extension| * extension.oid == 'authorityInfoAccess' * end * * descriptions = authority_info_access.value.split "\n" * ocsp = descriptions.find do |description| * description.start_with? 'OCSP' * end * * require 'uri' * * ocsp_uri = URI ocsp[/URI:(.*)/, 1] * * To submit the request we'll POST the request to the OCSP URI (per RFC * 2560). Note that we only handle HTTP requests and don't handle any * redirects in this example, so this is insufficient for serious use. * * require 'net/http' * * http_response = * Net::HTTP.start ocsp_uri.hostname, ocsp.port do |http| * http.post ocsp_uri.path, request.to_der, * 'content-type' => 'application/ocsp-request' * end * * response = OpenSSL::OCSP::Response.new http_response.body * response_basic = response.basic * * First we check if the response has a valid signature. Without a valid * signature we cannot trust it. If you get a failure here you may be * missing a system certificate store or may be missing the intermediate * certificates. * * store = OpenSSL::X509::Store.new * store.set_default_paths * * unless response_basic.verify [], store then * raise 'response is not signed by a trusted certificate' * end * * The response contains the status information (success/fail). We can * display the status as a string: * * puts response.status_string #=> successful * * Next we need to know the response details to determine if the response * matches our request. First we check the nonce. Again, not all CAs * support a nonce. See Request#check_nonce for the meanings of the * return values. * * p request.check_nonce basic_response #=> value from -1 to 3 * * Then extract the status information for the certificate from the basic * response. * * single_response = basic_response.find_response(certificate_id) * * unless single_response * raise 'basic_response does not have the status for the certificiate' * end * * Then check the validity. A status issued in the future must be rejected. * * unless single_response.check_validity * raise 'this_update is in the future or next_update time has passed' * end * * case single_response.cert_status * when OpenSSL::OCSP::V_CERTSTATUS_GOOD * puts 'certificate is still valid' * when OpenSSL::OCSP::V_CERTSTATUS_REVOKED * puts "certificate has been revoked at #{single_response.revocation_time}" * when OpenSSL::OCSP::V_CERTSTATUS_UNKNOWN * puts 'responder doesn't know about the certificate' * end */ mOCSP = rb_define_module_under(mOSSL, "OCSP"); /* * OCSP error class. */ eOCSPError = rb_define_class_under(mOCSP, "OCSPError", eOSSLError); /* * An OpenSSL::OCSP::Request contains the certificate information for * determining if a certificate has been revoked or not. A Request can be * created for a certificate or from a DER-encoded request created * elsewhere. */ cOCSPReq = rb_define_class_under(mOCSP, "Request", rb_cObject); rb_define_alloc_func(cOCSPReq, ossl_ocspreq_alloc); rb_define_copy_func(cOCSPReq, ossl_ocspreq_initialize_copy); rb_define_method(cOCSPReq, "initialize", ossl_ocspreq_initialize, -1); rb_define_method(cOCSPReq, "add_nonce", ossl_ocspreq_add_nonce, -1); rb_define_method(cOCSPReq, "check_nonce", ossl_ocspreq_check_nonce, 1); rb_define_method(cOCSPReq, "add_certid", ossl_ocspreq_add_certid, 1); rb_define_method(cOCSPReq, "certid", ossl_ocspreq_get_certid, 0); rb_define_method(cOCSPReq, "sign", ossl_ocspreq_sign, -1); rb_define_method(cOCSPReq, "verify", ossl_ocspreq_verify, -1); rb_define_method(cOCSPReq, "to_der", ossl_ocspreq_to_der, 0); /* * An OpenSSL::OCSP::Response contains the status of a certificate check * which is created from an OpenSSL::OCSP::Request. */ cOCSPRes = rb_define_class_under(mOCSP, "Response", rb_cObject); rb_define_singleton_method(cOCSPRes, "create", ossl_ocspres_s_create, 2); rb_define_alloc_func(cOCSPRes, ossl_ocspres_alloc); rb_define_copy_func(cOCSPRes, ossl_ocspres_initialize_copy); rb_define_method(cOCSPRes, "initialize", ossl_ocspres_initialize, -1); rb_define_method(cOCSPRes, "status", ossl_ocspres_status, 0); rb_define_method(cOCSPRes, "status_string", ossl_ocspres_status_string, 0); rb_define_method(cOCSPRes, "basic", ossl_ocspres_get_basic, 0); rb_define_method(cOCSPRes, "to_der", ossl_ocspres_to_der, 0); /* * An OpenSSL::OCSP::BasicResponse contains the status of a certificate * check which is created from an OpenSSL::OCSP::Request. A * BasicResponse is more detailed than a Response. */ cOCSPBasicRes = rb_define_class_under(mOCSP, "BasicResponse", rb_cObject); rb_define_alloc_func(cOCSPBasicRes, ossl_ocspbres_alloc); rb_define_copy_func(cOCSPBasicRes, ossl_ocspbres_initialize_copy); rb_define_method(cOCSPBasicRes, "initialize", ossl_ocspbres_initialize, -1); rb_define_method(cOCSPBasicRes, "copy_nonce", ossl_ocspbres_copy_nonce, 1); rb_define_method(cOCSPBasicRes, "add_nonce", ossl_ocspbres_add_nonce, -1); rb_define_method(cOCSPBasicRes, "add_status", ossl_ocspbres_add_status, 7); rb_define_method(cOCSPBasicRes, "status", ossl_ocspbres_get_status, 0); rb_define_method(cOCSPBasicRes, "responses", ossl_ocspbres_get_responses, 0); rb_define_method(cOCSPBasicRes, "find_response", ossl_ocspbres_find_response, 1); rb_define_method(cOCSPBasicRes, "sign", ossl_ocspbres_sign, -1); rb_define_method(cOCSPBasicRes, "verify", ossl_ocspbres_verify, -1); rb_define_method(cOCSPBasicRes, "to_der", ossl_ocspbres_to_der, 0); /* * An OpenSSL::OCSP::SingleResponse represents an OCSP SingleResponse * structure, which contains the basic information of the status of the * certificate. */ cOCSPSingleRes = rb_define_class_under(mOCSP, "SingleResponse", rb_cObject); rb_define_alloc_func(cOCSPSingleRes, ossl_ocspsres_alloc); rb_define_copy_func(cOCSPSingleRes, ossl_ocspsres_initialize_copy); rb_define_method(cOCSPSingleRes, "initialize", ossl_ocspsres_initialize, 1); rb_define_method(cOCSPSingleRes, "check_validity", ossl_ocspsres_check_validity, -1); rb_define_method(cOCSPSingleRes, "certid", ossl_ocspsres_get_certid, 0); rb_define_method(cOCSPSingleRes, "cert_status", ossl_ocspsres_get_cert_status, 0); rb_define_method(cOCSPSingleRes, "this_update", ossl_ocspsres_get_this_update, 0); rb_define_method(cOCSPSingleRes, "next_update", ossl_ocspsres_get_next_update, 0); rb_define_method(cOCSPSingleRes, "revocation_time", ossl_ocspsres_get_revocation_time, 0); rb_define_method(cOCSPSingleRes, "revocation_reason", ossl_ocspsres_get_revocation_reason, 0); rb_define_method(cOCSPSingleRes, "extensions", ossl_ocspsres_get_extensions, 0); rb_define_method(cOCSPSingleRes, "to_der", ossl_ocspsres_to_der, 0); /* * An OpenSSL::OCSP::CertificateId identifies a certificate to the CA so * that a status check can be performed. */ cOCSPCertId = rb_define_class_under(mOCSP, "CertificateId", rb_cObject); rb_define_alloc_func(cOCSPCertId, ossl_ocspcid_alloc); rb_define_copy_func(cOCSPCertId, ossl_ocspcid_initialize_copy); rb_define_method(cOCSPCertId, "initialize", ossl_ocspcid_initialize, -1); rb_define_method(cOCSPCertId, "cmp", ossl_ocspcid_cmp, 1); rb_define_method(cOCSPCertId, "cmp_issuer", ossl_ocspcid_cmp_issuer, 1); rb_define_method(cOCSPCertId, "serial", ossl_ocspcid_get_serial, 0); rb_define_method(cOCSPCertId, "issuer_name_hash", ossl_ocspcid_get_issuer_name_hash, 0); rb_define_method(cOCSPCertId, "issuer_key_hash", ossl_ocspcid_get_issuer_key_hash, 0); rb_define_method(cOCSPCertId, "hash_algorithm", ossl_ocspcid_get_hash_algorithm, 0); rb_define_method(cOCSPCertId, "to_der", ossl_ocspcid_to_der, 0); /* Internal error in issuer */ rb_define_const(mOCSP, "RESPONSE_STATUS_INTERNALERROR", INT2NUM(OCSP_RESPONSE_STATUS_INTERNALERROR)); /* Illegal confirmation request */ rb_define_const(mOCSP, "RESPONSE_STATUS_MALFORMEDREQUEST", INT2NUM(OCSP_RESPONSE_STATUS_MALFORMEDREQUEST)); /* The certificate was revoked for an unknown reason */ rb_define_const(mOCSP, "REVOKED_STATUS_NOSTATUS", INT2NUM(OCSP_REVOKED_STATUS_NOSTATUS)); /* You must sign the request and resubmit */ rb_define_const(mOCSP, "RESPONSE_STATUS_SIGREQUIRED", INT2NUM(OCSP_RESPONSE_STATUS_SIGREQUIRED)); /* Response has valid confirmations */ rb_define_const(mOCSP, "RESPONSE_STATUS_SUCCESSFUL", INT2NUM(OCSP_RESPONSE_STATUS_SUCCESSFUL)); /* Try again later */ rb_define_const(mOCSP, "RESPONSE_STATUS_TRYLATER", INT2NUM(OCSP_RESPONSE_STATUS_TRYLATER)); /* The certificate subject's name or other information changed */ rb_define_const(mOCSP, "REVOKED_STATUS_AFFILIATIONCHANGED", INT2NUM(OCSP_REVOKED_STATUS_AFFILIATIONCHANGED)); /* This CA certificate was revoked due to a key compromise */ rb_define_const(mOCSP, "REVOKED_STATUS_CACOMPROMISE", INT2NUM(OCSP_REVOKED_STATUS_CACOMPROMISE)); /* The certificate is on hold */ rb_define_const(mOCSP, "REVOKED_STATUS_CERTIFICATEHOLD", INT2NUM(OCSP_REVOKED_STATUS_CERTIFICATEHOLD)); /* The certificate is no longer needed */ rb_define_const(mOCSP, "REVOKED_STATUS_CESSATIONOFOPERATION", INT2NUM(OCSP_REVOKED_STATUS_CESSATIONOFOPERATION)); /* The certificate was revoked due to a key compromise */ rb_define_const(mOCSP, "REVOKED_STATUS_KEYCOMPROMISE", INT2NUM(OCSP_REVOKED_STATUS_KEYCOMPROMISE)); /* The certificate was previously on hold and should now be removed from * the CRL */ rb_define_const(mOCSP, "REVOKED_STATUS_REMOVEFROMCRL", INT2NUM(OCSP_REVOKED_STATUS_REMOVEFROMCRL)); /* The certificate was superseded by a new certificate */ rb_define_const(mOCSP, "REVOKED_STATUS_SUPERSEDED", INT2NUM(OCSP_REVOKED_STATUS_SUPERSEDED)); /* Your request is unauthorized. */ rb_define_const(mOCSP, "RESPONSE_STATUS_UNAUTHORIZED", INT2NUM(OCSP_RESPONSE_STATUS_UNAUTHORIZED)); /* The certificate was revoked for an unspecified reason */ rb_define_const(mOCSP, "REVOKED_STATUS_UNSPECIFIED", INT2NUM(OCSP_REVOKED_STATUS_UNSPECIFIED)); /* Do not include certificates in the response */ rb_define_const(mOCSP, "NOCERTS", INT2NUM(OCSP_NOCERTS)); /* Do not search certificates contained in the response for a signer */ rb_define_const(mOCSP, "NOINTERN", INT2NUM(OCSP_NOINTERN)); /* Do not check the signature on the response */ rb_define_const(mOCSP, "NOSIGS", INT2NUM(OCSP_NOSIGS)); /* Do not verify the certificate chain on the response */ rb_define_const(mOCSP, "NOCHAIN", INT2NUM(OCSP_NOCHAIN)); /* Do not verify the response at all */ rb_define_const(mOCSP, "NOVERIFY", INT2NUM(OCSP_NOVERIFY)); /* Do not check trust */ rb_define_const(mOCSP, "NOEXPLICIT", INT2NUM(OCSP_NOEXPLICIT)); /* (This flag is not used by OpenSSL 1.0.1g) */ rb_define_const(mOCSP, "NOCASIGN", INT2NUM(OCSP_NOCASIGN)); /* (This flag is not used by OpenSSL 1.0.1g) */ rb_define_const(mOCSP, "NODELEGATED", INT2NUM(OCSP_NODELEGATED)); /* Do not make additional signing certificate checks */ rb_define_const(mOCSP, "NOCHECKS", INT2NUM(OCSP_NOCHECKS)); /* Do not verify additional certificates */ rb_define_const(mOCSP, "TRUSTOTHER", INT2NUM(OCSP_TRUSTOTHER)); /* Identify the response by signing the certificate key ID */ rb_define_const(mOCSP, "RESPID_KEY", INT2NUM(OCSP_RESPID_KEY)); /* Do not include producedAt time in response */ rb_define_const(mOCSP, "NOTIME", INT2NUM(OCSP_NOTIME)); /* Indicates the certificate is not revoked but does not necessarily mean * the certificate was issued or that this response is within the * certificate's validity interval */ rb_define_const(mOCSP, "V_CERTSTATUS_GOOD", INT2NUM(V_OCSP_CERTSTATUS_GOOD)); /* Indicates the certificate has been revoked either permanently or * temporarily (on hold). */ rb_define_const(mOCSP, "V_CERTSTATUS_REVOKED", INT2NUM(V_OCSP_CERTSTATUS_REVOKED)); /* Indicates the responder does not know about the certificate being * requested. */ rb_define_const(mOCSP, "V_CERTSTATUS_UNKNOWN", INT2NUM(V_OCSP_CERTSTATUS_UNKNOWN)); /* The responder ID is based on the key name. */ rb_define_const(mOCSP, "V_RESPID_NAME", INT2NUM(V_OCSP_RESPID_NAME)); /* The responder ID is based on the public key. */ rb_define_const(mOCSP, "V_RESPID_KEY", INT2NUM(V_OCSP_RESPID_KEY)); } #else void Init_ossl_ocsp(void) { } #endif openssl-2.0.9/ext/openssl/ossl_ocsp.h000066400000000000000000000007771336157045000176640ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2003 Michal Rokos * Copyright (C) 2003 GOTOU Yuuzou * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_OCSP_H_) #define _OSSL_OCSP_H_ #if !defined(OPENSSL_NO_OCSP) extern VALUE mOCSP; extern VALUE cOPCSReq; extern VALUE cOPCSRes; extern VALUE cOPCSBasicRes; #endif void Init_ossl_ocsp(void); #endif /* _OSSL_OCSP_H_ */ openssl-2.0.9/ext/openssl/ossl_pkcs12.c000066400000000000000000000165301336157045000200100ustar00rootroot00000000000000/* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define NewPKCS12(klass) \ TypedData_Wrap_Struct((klass), &ossl_pkcs12_type, 0) #define SetPKCS12(obj, p12) do { \ if(!(p12)) ossl_raise(rb_eRuntimeError, "PKCS12 wasn't initialized."); \ RTYPEDDATA_DATA(obj) = (p12); \ } while (0) #define GetPKCS12(obj, p12) do { \ TypedData_Get_Struct((obj), PKCS12, &ossl_pkcs12_type, (p12)); \ if(!(p12)) ossl_raise(rb_eRuntimeError, "PKCS12 wasn't initialized."); \ } while (0) #define SafeGetPKCS12(obj, p12) do { \ OSSL_Check_Kind((obj), cPKCS12); \ GetPKCS12((obj), (p12)); \ } while (0) #define ossl_pkcs12_set_key(o,v) rb_iv_set((o), "@key", (v)) #define ossl_pkcs12_set_cert(o,v) rb_iv_set((o), "@certificate", (v)) #define ossl_pkcs12_set_ca_certs(o,v) rb_iv_set((o), "@ca_certs", (v)) #define ossl_pkcs12_get_key(o) rb_iv_get((o), "@key") #define ossl_pkcs12_get_cert(o) rb_iv_get((o), "@certificate") #define ossl_pkcs12_get_ca_certs(o) rb_iv_get((o), "@ca_certs") /* * Classes */ VALUE cPKCS12; VALUE ePKCS12Error; /* * Private */ static void ossl_pkcs12_free(void *ptr) { PKCS12_free(ptr); } static const rb_data_type_t ossl_pkcs12_type = { "OpenSSL/PKCS12", { 0, ossl_pkcs12_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static VALUE ossl_pkcs12_s_allocate(VALUE klass) { PKCS12 *p12; VALUE obj; obj = NewPKCS12(klass); if(!(p12 = PKCS12_new())) ossl_raise(ePKCS12Error, NULL); SetPKCS12(obj, p12); return obj; } static VALUE ossl_pkcs12_initialize_copy(VALUE self, VALUE other) { PKCS12 *p12, *p12_old, *p12_new; rb_check_frozen(self); GetPKCS12(self, p12_old); SafeGetPKCS12(other, p12); p12_new = ASN1_dup((i2d_of_void *)i2d_PKCS12, (d2i_of_void *)d2i_PKCS12, (char *)p12); if (!p12_new) ossl_raise(ePKCS12Error, "ASN1_dup"); SetPKCS12(self, p12_new); PKCS12_free(p12_old); return self; } /* * call-seq: * PKCS12.create(pass, name, key, cert [, ca, [, key_pbe [, cert_pbe [, key_iter [, mac_iter [, keytype]]]]]]) * * === Parameters * * +pass+ - string * * +name+ - A string describing the key. * * +key+ - Any PKey. * * +cert+ - A X509::Certificate. * * The public_key portion of the certificate must contain a valid public key. * * The not_before and not_after fields must be filled in. * * +ca+ - An optional array of X509::Certificate's. * * +key_pbe+ - string * * +cert_pbe+ - string * * +key_iter+ - integer * * +mac_iter+ - integer * * +keytype+ - An integer representing an MSIE specific extension. * * Any optional arguments may be supplied as nil to preserve the OpenSSL defaults. * * See the OpenSSL documentation for PKCS12_create(). */ static VALUE ossl_pkcs12_s_create(int argc, VALUE *argv, VALUE self) { VALUE pass, name, pkey, cert, ca, key_nid, cert_nid, key_iter, mac_iter, keytype; VALUE obj; char *passphrase, *friendlyname; EVP_PKEY *key; X509 *x509; STACK_OF(X509) *x509s; int nkey = 0, ncert = 0, kiter = 0, miter = 0, ktype = 0; PKCS12 *p12; rb_scan_args(argc, argv, "46", &pass, &name, &pkey, &cert, &ca, &key_nid, &cert_nid, &key_iter, &mac_iter, &keytype); passphrase = NIL_P(pass) ? NULL : StringValueCStr(pass); friendlyname = NIL_P(name) ? NULL : StringValueCStr(name); key = GetPKeyPtr(pkey); x509 = GetX509CertPtr(cert); /* TODO: make a VALUE to nid function */ if (!NIL_P(key_nid)) { if ((nkey = OBJ_txt2nid(StringValueCStr(key_nid))) == NID_undef) ossl_raise(rb_eArgError, "Unknown PBE algorithm %"PRIsVALUE, key_nid); } if (!NIL_P(cert_nid)) { if ((ncert = OBJ_txt2nid(StringValueCStr(cert_nid))) == NID_undef) ossl_raise(rb_eArgError, "Unknown PBE algorithm %"PRIsVALUE, cert_nid); } if (!NIL_P(key_iter)) kiter = NUM2INT(key_iter); if (!NIL_P(mac_iter)) miter = NUM2INT(mac_iter); if (!NIL_P(keytype)) ktype = NUM2INT(keytype); obj = NewPKCS12(cPKCS12); x509s = NIL_P(ca) ? NULL : ossl_x509_ary2sk(ca); p12 = PKCS12_create(passphrase, friendlyname, key, x509, x509s, nkey, ncert, kiter, miter, ktype); sk_X509_pop_free(x509s, X509_free); if(!p12) ossl_raise(ePKCS12Error, NULL); SetPKCS12(obj, p12); ossl_pkcs12_set_key(obj, pkey); ossl_pkcs12_set_cert(obj, cert); ossl_pkcs12_set_ca_certs(obj, ca); return obj; } /* * call-seq: * PKCS12.new -> pkcs12 * PKCS12.new(str) -> pkcs12 * PKCS12.new(str, pass) -> pkcs12 * * === Parameters * * +str+ - Must be a DER encoded PKCS12 string. * * +pass+ - string */ static VALUE ossl_pkcs12_initialize(int argc, VALUE *argv, VALUE self) { BIO *in; VALUE arg, pass, pkey, cert, ca; char *passphrase; EVP_PKEY *key; X509 *x509; STACK_OF(X509) *x509s = NULL; int st = 0; PKCS12 *pkcs = DATA_PTR(self); if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) return self; passphrase = NIL_P(pass) ? NULL : StringValueCStr(pass); in = ossl_obj2bio(&arg); d2i_PKCS12_bio(in, &pkcs); DATA_PTR(self) = pkcs; BIO_free(in); pkey = cert = ca = Qnil; /* OpenSSL's bug; PKCS12_parse() puts errors even if it succeeds. * Fixed in OpenSSL 1.0.0t, 1.0.1p, 1.0.2d */ ERR_set_mark(); if(!PKCS12_parse(pkcs, passphrase, &key, &x509, &x509s)) ossl_raise(ePKCS12Error, "PKCS12_parse"); ERR_pop_to_mark(); if (key) { pkey = rb_protect((VALUE (*)(VALUE))ossl_pkey_new, (VALUE)key, &st); if (st) goto err; } if (x509) { cert = rb_protect((VALUE (*)(VALUE))ossl_x509_new, (VALUE)x509, &st); if (st) goto err; } if (x509s) { ca = rb_protect((VALUE (*)(VALUE))ossl_x509_sk2ary, (VALUE)x509s, &st); if (st) goto err; } err: X509_free(x509); sk_X509_pop_free(x509s, X509_free); ossl_pkcs12_set_key(self, pkey); ossl_pkcs12_set_cert(self, cert); ossl_pkcs12_set_ca_certs(self, ca); if(st) rb_jump_tag(st); return self; } static VALUE ossl_pkcs12_to_der(VALUE self) { PKCS12 *p12; VALUE str; long len; unsigned char *p; GetPKCS12(self, p12); if((len = i2d_PKCS12(p12, NULL)) <= 0) ossl_raise(ePKCS12Error, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_PKCS12(p12, &p) <= 0) ossl_raise(ePKCS12Error, NULL); ossl_str_adjust(str, p); return str; } void Init_ossl_pkcs12(void) { #undef rb_intern #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif /* * Defines a file format commonly used to store private keys with * accompanying public key certificates, protected with a password-based * symmetric key. */ cPKCS12 = rb_define_class_under(mOSSL, "PKCS12", rb_cObject); ePKCS12Error = rb_define_class_under(cPKCS12, "PKCS12Error", eOSSLError); rb_define_singleton_method(cPKCS12, "create", ossl_pkcs12_s_create, -1); rb_define_alloc_func(cPKCS12, ossl_pkcs12_s_allocate); rb_define_copy_func(cPKCS12, ossl_pkcs12_initialize_copy); rb_attr(cPKCS12, rb_intern("key"), 1, 0, Qfalse); rb_attr(cPKCS12, rb_intern("certificate"), 1, 0, Qfalse); rb_attr(cPKCS12, rb_intern("ca_certs"), 1, 0, Qfalse); rb_define_method(cPKCS12, "initialize", ossl_pkcs12_initialize, -1); rb_define_method(cPKCS12, "to_der", ossl_pkcs12_to_der, 0); } openssl-2.0.9/ext/openssl/ossl_pkcs12.h000066400000000000000000000004041336157045000200060ustar00rootroot00000000000000/* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_PKCS12_H_) #define _OSSL_PKCS12_H_ extern VALUE cPKCS12; extern VALUE ePKCS12Error; void Init_ossl_pkcs12(void); #endif /* _OSSL_PKCS12_H_ */ openssl-2.0.9/ext/openssl/ossl_pkcs5.c000066400000000000000000000145751336157045000177410ustar00rootroot00000000000000/* * Copyright (C) 2007 Technorama Ltd. */ #include "ossl.h" VALUE mPKCS5; VALUE ePKCS5; #ifdef HAVE_PKCS5_PBKDF2_HMAC /* * call-seq: * PKCS5.pbkdf2_hmac(pass, salt, iter, keylen, digest) => string * * === Parameters * * +pass+ - string * * +salt+ - string - should be at least 8 bytes long. * * +iter+ - integer - should be greater than 1000. 20000 is better. * * +keylen+ - integer * * +digest+ - a string or OpenSSL::Digest object. * * Available in OpenSSL >= 1.0.0. * * Digests other than SHA1 may not be supported by other cryptography libraries. */ static VALUE ossl_pkcs5_pbkdf2_hmac(VALUE self, VALUE pass, VALUE salt, VALUE iter, VALUE keylen, VALUE digest) { VALUE str; const EVP_MD *md; int len = NUM2INT(keylen); StringValue(pass); StringValue(salt); md = GetDigestPtr(digest); str = rb_str_new(0, len); if (PKCS5_PBKDF2_HMAC(RSTRING_PTR(pass), RSTRING_LENINT(pass), (unsigned char *)RSTRING_PTR(salt), RSTRING_LENINT(salt), NUM2INT(iter), md, len, (unsigned char *)RSTRING_PTR(str)) != 1) ossl_raise(ePKCS5, "PKCS5_PBKDF2_HMAC"); return str; } #else #define ossl_pkcs5_pbkdf2_hmac rb_f_notimplement #endif /* * call-seq: * PKCS5.pbkdf2_hmac_sha1(pass, salt, iter, keylen) => string * * === Parameters * * +pass+ - string * * +salt+ - string - should be at least 8 bytes long. * * +iter+ - integer - should be greater than 1000. 20000 is better. * * +keylen+ - integer * * This method is available in almost any version of OpenSSL. * * Conforms to RFC 2898. */ static VALUE ossl_pkcs5_pbkdf2_hmac_sha1(VALUE self, VALUE pass, VALUE salt, VALUE iter, VALUE keylen) { VALUE str; int len = NUM2INT(keylen); StringValue(pass); StringValue(salt); str = rb_str_new(0, len); if (PKCS5_PBKDF2_HMAC_SHA1(RSTRING_PTR(pass), RSTRING_LENINT(pass), (const unsigned char *)RSTRING_PTR(salt), RSTRING_LENINT(salt), NUM2INT(iter), len, (unsigned char *)RSTRING_PTR(str)) != 1) ossl_raise(ePKCS5, "PKCS5_PBKDF2_HMAC_SHA1"); return str; } void Init_ossl_pkcs5(void) { #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif /* Document-class: OpenSSL::PKCS5 * * Provides password-based encryption functionality based on PKCS#5. * Typically used for securely deriving arbitrary length symmetric keys * to be used with an OpenSSL::Cipher from passwords. Another use case * is for storing passwords: Due to the ability to tweak the effort of * computation by increasing the iteration count, computation can be * slowed down artificially in order to render possible attacks infeasible. * * PKCS5 offers support for PBKDF2 with an OpenSSL::Digest::SHA1-based * HMAC, or an arbitrary Digest if the underlying version of OpenSSL * already supports it (>= 1.0.0). * * === Parameters * ==== Password * Typically an arbitrary String that represents the password to be used * for deriving a key. * ==== Salt * Prevents attacks based on dictionaries of common passwords. It is a * public value that can be safely stored along with the password (e.g. * if PBKDF2 is used for password storage). For maximum security, a fresh, * random salt should be generated for each stored password. According * to PKCS#5, a salt should be at least 8 bytes long. * ==== Iteration Count * Allows to tweak the length that the actual computation will take. The * larger the iteration count, the longer it will take. * ==== Key Length * Specifies the length in bytes of the output that will be generated. * Typically, the key length should be larger than or equal to the output * length of the underlying digest function, otherwise an attacker could * simply try to brute-force the key. According to PKCS#5, security is * limited by the output length of the underlying digest function, i.e. * security is not improved if a key length strictly larger than the * digest output length is chosen. Therefore, when using PKCS5 for * password storage, it suffices to store values equal to the digest * output length, nothing is gained by storing larger values. * * == Examples * === Generating a 128 bit key for a Cipher (e.g. AES) * pass = "secret" * salt = OpenSSL::Random.random_bytes(16) * iter = 20000 * key_len = 16 * key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(pass, salt, iter, key_len) * * === Storing Passwords * pass = "secret" * salt = OpenSSL::Random.random_bytes(16) #store this with the generated value * iter = 20000 * digest = OpenSSL::Digest::SHA256.new * len = digest.digest_length * #the final value to be stored * value = OpenSSL::PKCS5.pbkdf2_hmac(pass, salt, iter, len, digest) * * === Important Note on Checking Passwords * When comparing passwords provided by the user with previously stored * values, a common mistake made is comparing the two values using "==". * Typically, "==" short-circuits on evaluation, and is therefore * vulnerable to timing attacks. The proper way is to use a method that * always takes the same amount of time when comparing two values, thus * not leaking any information to potential attackers. To compare two * values, the following could be used: * def eql_time_cmp(a, b) * unless a.length == b.length * return false * end * cmp = b.bytes.to_a * result = 0 * a.bytes.each_with_index {|c,i| * result |= c ^ cmp[i] * } * result == 0 * end * Please note that the premature return in case of differing lengths * typically does not leak valuable information - when using PKCS#5, the * length of the values to be compared is of fixed size. */ mPKCS5 = rb_define_module_under(mOSSL, "PKCS5"); /* Document-class: OpenSSL::PKCS5::PKCS5Error * * Generic Exception class that is raised if an error occurs during a * computation. */ ePKCS5 = rb_define_class_under(mPKCS5, "PKCS5Error", eOSSLError); rb_define_module_function(mPKCS5, "pbkdf2_hmac", ossl_pkcs5_pbkdf2_hmac, 5); rb_define_module_function(mPKCS5, "pbkdf2_hmac_sha1", ossl_pkcs5_pbkdf2_hmac_sha1, 4); } openssl-2.0.9/ext/openssl/ossl_pkcs5.h000066400000000000000000000001561336157045000177340ustar00rootroot00000000000000#if !defined(_OSSL_PKCS5_H_) #define _OSSL_PKCS5_H_ void Init_ossl_pkcs5(void); #endif /* _OSSL_PKCS5_H_ */ openssl-2.0.9/ext/openssl/ossl_pkcs7.c000066400000000000000000000650271336157045000177410ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define NewPKCS7(klass) \ TypedData_Wrap_Struct((klass), &ossl_pkcs7_type, 0) #define SetPKCS7(obj, pkcs7) do { \ if (!(pkcs7)) { \ ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \ } \ RTYPEDDATA_DATA(obj) = (pkcs7); \ } while (0) #define GetPKCS7(obj, pkcs7) do { \ TypedData_Get_Struct((obj), PKCS7, &ossl_pkcs7_type, (pkcs7)); \ if (!(pkcs7)) { \ ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \ } \ } while (0) #define SafeGetPKCS7(obj, pkcs7) do { \ OSSL_Check_Kind((obj), cPKCS7); \ GetPKCS7((obj), (pkcs7)); \ } while (0) #define NewPKCS7si(klass) \ TypedData_Wrap_Struct((klass), &ossl_pkcs7_signer_info_type, 0) #define SetPKCS7si(obj, p7si) do { \ if (!(p7si)) { \ ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \ } \ RTYPEDDATA_DATA(obj) = (p7si); \ } while (0) #define GetPKCS7si(obj, p7si) do { \ TypedData_Get_Struct((obj), PKCS7_SIGNER_INFO, &ossl_pkcs7_signer_info_type, (p7si)); \ if (!(p7si)) { \ ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \ } \ } while (0) #define SafeGetPKCS7si(obj, p7si) do { \ OSSL_Check_Kind((obj), cPKCS7Signer); \ GetPKCS7si((obj), (p7si)); \ } while (0) #define NewPKCS7ri(klass) \ TypedData_Wrap_Struct((klass), &ossl_pkcs7_recip_info_type, 0) #define SetPKCS7ri(obj, p7ri) do { \ if (!(p7ri)) { \ ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \ } \ RTYPEDDATA_DATA(obj) = (p7ri); \ } while (0) #define GetPKCS7ri(obj, p7ri) do { \ TypedData_Get_Struct((obj), PKCS7_RECIP_INFO, &ossl_pkcs7_recip_info_type, (p7ri)); \ if (!(p7ri)) { \ ossl_raise(rb_eRuntimeError, "PKCS7ri wasn't initialized."); \ } \ } while (0) #define SafeGetPKCS7ri(obj, p7ri) do { \ OSSL_Check_Kind((obj), cPKCS7Recipient); \ GetPKCS7ri((obj), (p7ri)); \ } while (0) #define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0])) #define ossl_pkcs7_set_data(o,v) rb_iv_set((o), "@data", (v)) #define ossl_pkcs7_get_data(o) rb_iv_get((o), "@data") #define ossl_pkcs7_set_err_string(o,v) rb_iv_set((o), "@error_string", (v)) #define ossl_pkcs7_get_err_string(o) rb_iv_get((o), "@error_string") /* * Classes */ VALUE cPKCS7; VALUE cPKCS7Signer; VALUE cPKCS7Recipient; VALUE ePKCS7Error; static void ossl_pkcs7_free(void *ptr) { PKCS7_free(ptr); } static const rb_data_type_t ossl_pkcs7_type = { "OpenSSL/PKCS7", { 0, ossl_pkcs7_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static void ossl_pkcs7_signer_info_free(void *ptr) { PKCS7_SIGNER_INFO_free(ptr); } static const rb_data_type_t ossl_pkcs7_signer_info_type = { "OpenSSL/PKCS7/SIGNER_INFO", { 0, ossl_pkcs7_signer_info_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static void ossl_pkcs7_recip_info_free(void *ptr) { PKCS7_RECIP_INFO_free(ptr); } static const rb_data_type_t ossl_pkcs7_recip_info_type = { "OpenSSL/PKCS7/RECIP_INFO", { 0, ossl_pkcs7_recip_info_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * Public * (MADE PRIVATE UNTIL SOMEBODY WILL NEED THEM) */ static PKCS7_SIGNER_INFO * ossl_PKCS7_SIGNER_INFO_dup(const PKCS7_SIGNER_INFO *si) { return (PKCS7_SIGNER_INFO *)ASN1_dup((i2d_of_void *)i2d_PKCS7_SIGNER_INFO, (d2i_of_void *)d2i_PKCS7_SIGNER_INFO, (char *)si); } static PKCS7_RECIP_INFO * ossl_PKCS7_RECIP_INFO_dup(const PKCS7_RECIP_INFO *si) { return (PKCS7_RECIP_INFO *)ASN1_dup((i2d_of_void *)i2d_PKCS7_RECIP_INFO, (d2i_of_void *)d2i_PKCS7_RECIP_INFO, (char *)si); } static VALUE ossl_pkcs7si_new(PKCS7_SIGNER_INFO *p7si) { PKCS7_SIGNER_INFO *pkcs7; VALUE obj; obj = NewPKCS7si(cPKCS7Signer); pkcs7 = p7si ? ossl_PKCS7_SIGNER_INFO_dup(p7si) : PKCS7_SIGNER_INFO_new(); if (!pkcs7) ossl_raise(ePKCS7Error, NULL); SetPKCS7si(obj, pkcs7); return obj; } static PKCS7_SIGNER_INFO * DupPKCS7SignerPtr(VALUE obj) { PKCS7_SIGNER_INFO *p7si, *pkcs7; SafeGetPKCS7si(obj, p7si); if (!(pkcs7 = ossl_PKCS7_SIGNER_INFO_dup(p7si))) { ossl_raise(ePKCS7Error, NULL); } return pkcs7; } static VALUE ossl_pkcs7ri_new(PKCS7_RECIP_INFO *p7ri) { PKCS7_RECIP_INFO *pkcs7; VALUE obj; obj = NewPKCS7ri(cPKCS7Recipient); pkcs7 = p7ri ? ossl_PKCS7_RECIP_INFO_dup(p7ri) : PKCS7_RECIP_INFO_new(); if (!pkcs7) ossl_raise(ePKCS7Error, NULL); SetPKCS7ri(obj, pkcs7); return obj; } static PKCS7_RECIP_INFO * DupPKCS7RecipientPtr(VALUE obj) { PKCS7_RECIP_INFO *p7ri, *pkcs7; SafeGetPKCS7ri(obj, p7ri); if (!(pkcs7 = ossl_PKCS7_RECIP_INFO_dup(p7ri))) { ossl_raise(ePKCS7Error, NULL); } return pkcs7; } /* * call-seq: * PKCS7.read_smime(string) => pkcs7 */ static VALUE ossl_pkcs7_s_read_smime(VALUE klass, VALUE arg) { BIO *in, *out; PKCS7 *pkcs7; VALUE ret, data; ret = NewPKCS7(cPKCS7); in = ossl_obj2bio(&arg); out = NULL; pkcs7 = SMIME_read_PKCS7(in, &out); BIO_free(in); if(!pkcs7) ossl_raise(ePKCS7Error, NULL); data = out ? ossl_membio2str(out) : Qnil; SetPKCS7(ret, pkcs7); ossl_pkcs7_set_data(ret, data); ossl_pkcs7_set_err_string(ret, Qnil); return ret; } /* * call-seq: * PKCS7.write_smime(pkcs7 [, data [, flags]]) => string */ static VALUE ossl_pkcs7_s_write_smime(int argc, VALUE *argv, VALUE klass) { VALUE pkcs7, data, flags; BIO *out, *in; PKCS7 *p7; VALUE str; int flg; rb_scan_args(argc, argv, "12", &pkcs7, &data, &flags); flg = NIL_P(flags) ? 0 : NUM2INT(flags); if(NIL_P(data)) data = ossl_pkcs7_get_data(pkcs7); SafeGetPKCS7(pkcs7, p7); if(!NIL_P(data) && PKCS7_is_detached(p7)) flg |= PKCS7_DETACHED; in = NIL_P(data) ? NULL : ossl_obj2bio(&data); if(!(out = BIO_new(BIO_s_mem()))){ BIO_free(in); ossl_raise(ePKCS7Error, NULL); } if(!SMIME_write_PKCS7(out, p7, in, flg)){ BIO_free(out); BIO_free(in); ossl_raise(ePKCS7Error, NULL); } BIO_free(in); str = ossl_membio2str(out); return str; } /* * call-seq: * PKCS7.sign(cert, key, data, [, certs [, flags]]) => pkcs7 */ static VALUE ossl_pkcs7_s_sign(int argc, VALUE *argv, VALUE klass) { VALUE cert, key, data, certs, flags; X509 *x509; EVP_PKEY *pkey; BIO *in; STACK_OF(X509) *x509s; int flg, status = 0; PKCS7 *pkcs7; VALUE ret; rb_scan_args(argc, argv, "32", &cert, &key, &data, &certs, &flags); x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */ pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ flg = NIL_P(flags) ? 0 : NUM2INT(flags); ret = NewPKCS7(cPKCS7); in = ossl_obj2bio(&data); if(NIL_P(certs)) x509s = NULL; else{ x509s = ossl_protect_x509_ary2sk(certs, &status); if(status){ BIO_free(in); rb_jump_tag(status); } } if(!(pkcs7 = PKCS7_sign(x509, pkey, x509s, in, flg))){ BIO_free(in); sk_X509_pop_free(x509s, X509_free); ossl_raise(ePKCS7Error, NULL); } SetPKCS7(ret, pkcs7); ossl_pkcs7_set_data(ret, data); ossl_pkcs7_set_err_string(ret, Qnil); BIO_free(in); sk_X509_pop_free(x509s, X509_free); return ret; } /* * call-seq: * PKCS7.encrypt(certs, data, [, cipher [, flags]]) => pkcs7 */ static VALUE ossl_pkcs7_s_encrypt(int argc, VALUE *argv, VALUE klass) { VALUE certs, data, cipher, flags; STACK_OF(X509) *x509s; BIO *in; const EVP_CIPHER *ciph; int flg, status = 0; VALUE ret; PKCS7 *p7; rb_scan_args(argc, argv, "22", &certs, &data, &cipher, &flags); if(NIL_P(cipher)){ #if !defined(OPENSSL_NO_RC2) ciph = EVP_rc2_40_cbc(); #elif !defined(OPENSSL_NO_DES) ciph = EVP_des_ede3_cbc(); #elif !defined(OPENSSL_NO_RC2) ciph = EVP_rc2_40_cbc(); #elif !defined(OPENSSL_NO_AES) ciph = EVP_EVP_aes_128_cbc(); #else ossl_raise(ePKCS7Error, "Must specify cipher"); #endif } else ciph = GetCipherPtr(cipher); /* NO NEED TO DUP */ flg = NIL_P(flags) ? 0 : NUM2INT(flags); ret = NewPKCS7(cPKCS7); in = ossl_obj2bio(&data); x509s = ossl_protect_x509_ary2sk(certs, &status); if(status){ BIO_free(in); rb_jump_tag(status); } if(!(p7 = PKCS7_encrypt(x509s, in, (EVP_CIPHER*)ciph, flg))){ BIO_free(in); sk_X509_pop_free(x509s, X509_free); ossl_raise(ePKCS7Error, NULL); } BIO_free(in); SetPKCS7(ret, p7); ossl_pkcs7_set_data(ret, data); sk_X509_pop_free(x509s, X509_free); return ret; } static VALUE ossl_pkcs7_alloc(VALUE klass) { PKCS7 *pkcs7; VALUE obj; obj = NewPKCS7(klass); if (!(pkcs7 = PKCS7_new())) { ossl_raise(ePKCS7Error, NULL); } SetPKCS7(obj, pkcs7); return obj; } /* * call-seq: * PKCS7.new => pkcs7 * PKCS7.new(string) => pkcs7 * * Many methods in this class aren't documented. */ static VALUE ossl_pkcs7_initialize(int argc, VALUE *argv, VALUE self) { PKCS7 *p7, *pkcs = DATA_PTR(self); BIO *in; VALUE arg; if(rb_scan_args(argc, argv, "01", &arg) == 0) return self; arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); p7 = PEM_read_bio_PKCS7(in, &pkcs, NULL, NULL); if (!p7) { OSSL_BIO_reset(in); p7 = d2i_PKCS7_bio(in, &pkcs); if (!p7) { BIO_free(in); PKCS7_free(pkcs); DATA_PTR(self) = NULL; ossl_raise(rb_eArgError, "Could not parse the PKCS7"); } } DATA_PTR(self) = pkcs; BIO_free(in); ossl_pkcs7_set_data(self, Qnil); ossl_pkcs7_set_err_string(self, Qnil); return self; } static VALUE ossl_pkcs7_copy(VALUE self, VALUE other) { PKCS7 *a, *b, *pkcs7; rb_check_frozen(self); if (self == other) return self; GetPKCS7(self, a); SafeGetPKCS7(other, b); pkcs7 = PKCS7_dup(b); if (!pkcs7) { ossl_raise(ePKCS7Error, NULL); } DATA_PTR(self) = pkcs7; PKCS7_free(a); return self; } static int ossl_pkcs7_sym2typeid(VALUE sym) { int i, ret = Qnil; const char *s; size_t l; static const struct { char name[20]; int nid; } p7_type_tab[] = { { "signed", NID_pkcs7_signed }, { "data", NID_pkcs7_data }, { "signedAndEnveloped", NID_pkcs7_signedAndEnveloped }, { "enveloped", NID_pkcs7_enveloped }, { "encrypted", NID_pkcs7_encrypted }, { "digest", NID_pkcs7_digest }, }; if (SYMBOL_P(sym)) sym = rb_sym2str(sym); else StringValue(sym); RSTRING_GETMEM(sym, s, l); for(i = 0; ; i++){ if(i == numberof(p7_type_tab)) ossl_raise(ePKCS7Error, "unknown type \"%"PRIsVALUE"\"", sym); if(strlen(p7_type_tab[i].name) != l) continue; if(strcmp(p7_type_tab[i].name, s) == 0){ ret = p7_type_tab[i].nid; break; } } return ret; } /* * call-seq: * pkcs7.type = type => type */ static VALUE ossl_pkcs7_set_type(VALUE self, VALUE type) { PKCS7 *p7; GetPKCS7(self, p7); if(!PKCS7_set_type(p7, ossl_pkcs7_sym2typeid(type))) ossl_raise(ePKCS7Error, NULL); return type; } /* * call-seq: * pkcs7.type => string or nil */ static VALUE ossl_pkcs7_get_type(VALUE self) { PKCS7 *p7; GetPKCS7(self, p7); if(PKCS7_type_is_signed(p7)) return ID2SYM(rb_intern("signed")); if(PKCS7_type_is_encrypted(p7)) return ID2SYM(rb_intern("encrypted")); if(PKCS7_type_is_enveloped(p7)) return ID2SYM(rb_intern("enveloped")); if(PKCS7_type_is_signedAndEnveloped(p7)) return ID2SYM(rb_intern("signedAndEnveloped")); if(PKCS7_type_is_data(p7)) return ID2SYM(rb_intern("data")); return Qnil; } static VALUE ossl_pkcs7_set_detached(VALUE self, VALUE flag) { PKCS7 *p7; GetPKCS7(self, p7); if(flag != Qtrue && flag != Qfalse) ossl_raise(ePKCS7Error, "must specify a boolean"); if(!PKCS7_set_detached(p7, flag == Qtrue ? 1 : 0)) ossl_raise(ePKCS7Error, NULL); return flag; } static VALUE ossl_pkcs7_get_detached(VALUE self) { PKCS7 *p7; GetPKCS7(self, p7); return PKCS7_get_detached(p7) ? Qtrue : Qfalse; } static VALUE ossl_pkcs7_detached_p(VALUE self) { PKCS7 *p7; GetPKCS7(self, p7); return PKCS7_is_detached(p7) ? Qtrue : Qfalse; } static VALUE ossl_pkcs7_set_cipher(VALUE self, VALUE cipher) { PKCS7 *pkcs7; GetPKCS7(self, pkcs7); if (!PKCS7_set_cipher(pkcs7, GetCipherPtr(cipher))) { ossl_raise(ePKCS7Error, NULL); } return cipher; } static VALUE ossl_pkcs7_add_signer(VALUE self, VALUE signer) { PKCS7 *pkcs7; PKCS7_SIGNER_INFO *p7si; p7si = DupPKCS7SignerPtr(signer); /* NEED TO DUP */ GetPKCS7(self, pkcs7); if (!PKCS7_add_signer(pkcs7, p7si)) { PKCS7_SIGNER_INFO_free(p7si); ossl_raise(ePKCS7Error, "Could not add signer."); } if (PKCS7_type_is_signed(pkcs7)){ PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType, V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data)); } return self; } static VALUE ossl_pkcs7_get_signer(VALUE self) { PKCS7 *pkcs7; STACK_OF(PKCS7_SIGNER_INFO) *sk; PKCS7_SIGNER_INFO *si; int num, i; VALUE ary; GetPKCS7(self, pkcs7); if (!(sk = PKCS7_get_signer_info(pkcs7))) { OSSL_Debug("OpenSSL::PKCS7#get_signer_info == NULL!"); return rb_ary_new(); } if ((num = sk_PKCS7_SIGNER_INFO_num(sk)) < 0) { ossl_raise(ePKCS7Error, "Negative number of signers!"); } ary = rb_ary_new2(num); for (i=0; id.enveloped->recipientinfo; else if (PKCS7_type_is_signedAndEnveloped(pkcs7)) sk = pkcs7->d.signed_and_enveloped->recipientinfo; else sk = NULL; if (!sk) return rb_ary_new(); if ((num = sk_PKCS7_RECIP_INFO_num(sk)) < 0) { ossl_raise(ePKCS7Error, "Negative number of recipient!"); } ary = rb_ary_new2(num); for (i=0; itype); switch(i){ case NID_pkcs7_signed: certs = pkcs7->d.sign->cert; break; case NID_pkcs7_signedAndEnveloped: certs = pkcs7->d.signed_and_enveloped->cert; break; default: certs = NULL; } return certs; } static STACK_OF(X509_CRL) * pkcs7_get_crls(VALUE self) { PKCS7 *pkcs7; STACK_OF(X509_CRL) *crls; int i; GetPKCS7(self, pkcs7); i = OBJ_obj2nid(pkcs7->type); switch(i){ case NID_pkcs7_signed: crls = pkcs7->d.sign->crl; break; case NID_pkcs7_signedAndEnveloped: crls = pkcs7->d.signed_and_enveloped->crl; break; default: crls = NULL; } return crls; } static VALUE ossl_pkcs7_set_certs_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg)) { return ossl_pkcs7_add_certificate(arg, i); } static VALUE ossl_pkcs7_set_certificates(VALUE self, VALUE ary) { STACK_OF(X509) *certs; X509 *cert; certs = pkcs7_get_certs(self); while((cert = sk_X509_pop(certs))) X509_free(cert); rb_block_call(ary, rb_intern("each"), 0, 0, ossl_pkcs7_set_certs_i, self); return ary; } static VALUE ossl_pkcs7_get_certificates(VALUE self) { return ossl_x509_sk2ary(pkcs7_get_certs(self)); } static VALUE ossl_pkcs7_add_crl(VALUE self, VALUE crl) { PKCS7 *pkcs7; X509_CRL *x509crl; GetPKCS7(self, pkcs7); /* NO DUP needed! */ x509crl = GetX509CRLPtr(crl); if (!PKCS7_add_crl(pkcs7, x509crl)) { ossl_raise(ePKCS7Error, NULL); } return self; } static VALUE ossl_pkcs7_set_crls_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg)) { return ossl_pkcs7_add_crl(arg, i); } static VALUE ossl_pkcs7_set_crls(VALUE self, VALUE ary) { STACK_OF(X509_CRL) *crls; X509_CRL *crl; crls = pkcs7_get_crls(self); while((crl = sk_X509_CRL_pop(crls))) X509_CRL_free(crl); rb_block_call(ary, rb_intern("each"), 0, 0, ossl_pkcs7_set_crls_i, self); return ary; } static VALUE ossl_pkcs7_get_crls(VALUE self) { return ossl_x509crl_sk2ary(pkcs7_get_crls(self)); } static VALUE ossl_pkcs7_verify(int argc, VALUE *argv, VALUE self) { VALUE certs, store, indata, flags; STACK_OF(X509) *x509s; X509_STORE *x509st; int flg, ok, status = 0; BIO *in, *out; PKCS7 *p7; VALUE data; const char *msg; GetPKCS7(self, p7); rb_scan_args(argc, argv, "22", &certs, &store, &indata, &flags); x509st = GetX509StorePtr(store); flg = NIL_P(flags) ? 0 : NUM2INT(flags); if(NIL_P(indata)) indata = ossl_pkcs7_get_data(self); in = NIL_P(indata) ? NULL : ossl_obj2bio(&indata); if(NIL_P(certs)) x509s = NULL; else{ x509s = ossl_protect_x509_ary2sk(certs, &status); if(status){ BIO_free(in); rb_jump_tag(status); } } if(!(out = BIO_new(BIO_s_mem()))){ BIO_free(in); sk_X509_pop_free(x509s, X509_free); ossl_raise(ePKCS7Error, NULL); } ok = PKCS7_verify(p7, x509s, x509st, in, out, flg); BIO_free(in); sk_X509_pop_free(x509s, X509_free); if (ok < 0) ossl_raise(ePKCS7Error, "PKCS7_verify"); msg = ERR_reason_error_string(ERR_peek_error()); ossl_pkcs7_set_err_string(self, msg ? rb_str_new2(msg) : Qnil); ossl_clear_error(); data = ossl_membio2str(out); ossl_pkcs7_set_data(self, data); return (ok == 1) ? Qtrue : Qfalse; } static VALUE ossl_pkcs7_decrypt(int argc, VALUE *argv, VALUE self) { VALUE pkey, cert, flags; EVP_PKEY *key; X509 *x509; int flg; PKCS7 *p7; BIO *out; VALUE str; rb_scan_args(argc, argv, "21", &pkey, &cert, &flags); key = GetPrivPKeyPtr(pkey); /* NO NEED TO DUP */ x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */ flg = NIL_P(flags) ? 0 : NUM2INT(flags); GetPKCS7(self, p7); if(!(out = BIO_new(BIO_s_mem()))) ossl_raise(ePKCS7Error, NULL); if(!PKCS7_decrypt(p7, key, x509, out, flg)){ BIO_free(out); ossl_raise(ePKCS7Error, NULL); } str = ossl_membio2str(out); /* out will be free */ return str; } static VALUE ossl_pkcs7_add_data(VALUE self, VALUE data) { PKCS7 *pkcs7; BIO *out, *in; char buf[4096]; int len; GetPKCS7(self, pkcs7); if(PKCS7_type_is_signed(pkcs7)){ if(!PKCS7_content_new(pkcs7, NID_pkcs7_data)) ossl_raise(ePKCS7Error, NULL); } in = ossl_obj2bio(&data); if(!(out = PKCS7_dataInit(pkcs7, NULL))) goto err; for(;;){ if((len = BIO_read(in, buf, sizeof(buf))) <= 0) break; if(BIO_write(out, buf, len) != len) goto err; } if(!PKCS7_dataFinal(pkcs7, out)) goto err; ossl_pkcs7_set_data(self, Qnil); err: BIO_free_all(out); BIO_free(in); if(ERR_peek_error()){ ossl_raise(ePKCS7Error, NULL); } return data; } static VALUE ossl_pkcs7_to_der(VALUE self) { PKCS7 *pkcs7; VALUE str; long len; unsigned char *p; GetPKCS7(self, pkcs7); if((len = i2d_PKCS7(pkcs7, NULL)) <= 0) ossl_raise(ePKCS7Error, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_PKCS7(pkcs7, &p) <= 0) ossl_raise(ePKCS7Error, NULL); ossl_str_adjust(str, p); return str; } static VALUE ossl_pkcs7_to_pem(VALUE self) { PKCS7 *pkcs7; BIO *out; VALUE str; GetPKCS7(self, pkcs7); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(ePKCS7Error, NULL); } if (!PEM_write_bio_PKCS7(out, pkcs7)) { BIO_free(out); ossl_raise(ePKCS7Error, NULL); } str = ossl_membio2str(out); return str; } /* * SIGNER INFO */ static VALUE ossl_pkcs7si_alloc(VALUE klass) { PKCS7_SIGNER_INFO *p7si; VALUE obj; obj = NewPKCS7si(klass); if (!(p7si = PKCS7_SIGNER_INFO_new())) { ossl_raise(ePKCS7Error, NULL); } SetPKCS7si(obj, p7si); return obj; } static VALUE ossl_pkcs7si_initialize(VALUE self, VALUE cert, VALUE key, VALUE digest) { PKCS7_SIGNER_INFO *p7si; EVP_PKEY *pkey; X509 *x509; const EVP_MD *md; pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */ md = GetDigestPtr(digest); GetPKCS7si(self, p7si); if (!(PKCS7_SIGNER_INFO_set(p7si, x509, pkey, (EVP_MD*)md))) { ossl_raise(ePKCS7Error, NULL); } return self; } static VALUE ossl_pkcs7si_get_issuer(VALUE self) { PKCS7_SIGNER_INFO *p7si; GetPKCS7si(self, p7si); return ossl_x509name_new(p7si->issuer_and_serial->issuer); } static VALUE ossl_pkcs7si_get_serial(VALUE self) { PKCS7_SIGNER_INFO *p7si; GetPKCS7si(self, p7si); return asn1integer_to_num(p7si->issuer_and_serial->serial); } static VALUE ossl_pkcs7si_get_signed_time(VALUE self) { PKCS7_SIGNER_INFO *p7si; ASN1_TYPE *asn1obj; GetPKCS7si(self, p7si); if (!(asn1obj = PKCS7_get_signed_attribute(p7si, NID_pkcs9_signingTime))) { ossl_raise(ePKCS7Error, NULL); } if (asn1obj->type == V_ASN1_UTCTIME) { return asn1time_to_time(asn1obj->value.utctime); } /* * OR * ossl_raise(ePKCS7Error, "..."); * ? */ return Qnil; } /* * RECIPIENT INFO */ static VALUE ossl_pkcs7ri_alloc(VALUE klass) { PKCS7_RECIP_INFO *p7ri; VALUE obj; obj = NewPKCS7ri(klass); if (!(p7ri = PKCS7_RECIP_INFO_new())) { ossl_raise(ePKCS7Error, NULL); } SetPKCS7ri(obj, p7ri); return obj; } static VALUE ossl_pkcs7ri_initialize(VALUE self, VALUE cert) { PKCS7_RECIP_INFO *p7ri; X509 *x509; x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */ GetPKCS7ri(self, p7ri); if (!PKCS7_RECIP_INFO_set(p7ri, x509)) { ossl_raise(ePKCS7Error, NULL); } return self; } static VALUE ossl_pkcs7ri_get_issuer(VALUE self) { PKCS7_RECIP_INFO *p7ri; GetPKCS7ri(self, p7ri); return ossl_x509name_new(p7ri->issuer_and_serial->issuer); } static VALUE ossl_pkcs7ri_get_serial(VALUE self) { PKCS7_RECIP_INFO *p7ri; GetPKCS7ri(self, p7ri); return asn1integer_to_num(p7ri->issuer_and_serial->serial); } static VALUE ossl_pkcs7ri_get_enc_key(VALUE self) { PKCS7_RECIP_INFO *p7ri; GetPKCS7ri(self, p7ri); return asn1str_to_str(p7ri->enc_key); } /* * INIT */ void Init_ossl_pkcs7(void) { #undef rb_intern #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif cPKCS7 = rb_define_class_under(mOSSL, "PKCS7", rb_cObject); ePKCS7Error = rb_define_class_under(cPKCS7, "PKCS7Error", eOSSLError); rb_define_singleton_method(cPKCS7, "read_smime", ossl_pkcs7_s_read_smime, 1); rb_define_singleton_method(cPKCS7, "write_smime", ossl_pkcs7_s_write_smime, -1); rb_define_singleton_method(cPKCS7, "sign", ossl_pkcs7_s_sign, -1); rb_define_singleton_method(cPKCS7, "encrypt", ossl_pkcs7_s_encrypt, -1); rb_attr(cPKCS7, rb_intern("data"), 1, 0, Qfalse); rb_attr(cPKCS7, rb_intern("error_string"), 1, 1, Qfalse); rb_define_alloc_func(cPKCS7, ossl_pkcs7_alloc); rb_define_copy_func(cPKCS7, ossl_pkcs7_copy); rb_define_method(cPKCS7, "initialize", ossl_pkcs7_initialize, -1); rb_define_method(cPKCS7, "type=", ossl_pkcs7_set_type, 1); rb_define_method(cPKCS7, "type", ossl_pkcs7_get_type, 0); rb_define_method(cPKCS7, "detached=", ossl_pkcs7_set_detached, 1); rb_define_method(cPKCS7, "detached", ossl_pkcs7_get_detached, 0); rb_define_method(cPKCS7, "detached?", ossl_pkcs7_detached_p, 0); rb_define_method(cPKCS7, "cipher=", ossl_pkcs7_set_cipher, 1); rb_define_method(cPKCS7, "add_signer", ossl_pkcs7_add_signer, 1); rb_define_method(cPKCS7, "signers", ossl_pkcs7_get_signer, 0); rb_define_method(cPKCS7, "add_recipient", ossl_pkcs7_add_recipient, 1); rb_define_method(cPKCS7, "recipients", ossl_pkcs7_get_recipient, 0); rb_define_method(cPKCS7, "add_certificate", ossl_pkcs7_add_certificate, 1); rb_define_method(cPKCS7, "certificates=", ossl_pkcs7_set_certificates, 1); rb_define_method(cPKCS7, "certificates", ossl_pkcs7_get_certificates, 0); rb_define_method(cPKCS7, "add_crl", ossl_pkcs7_add_crl, 1); rb_define_method(cPKCS7, "crls=", ossl_pkcs7_set_crls, 1); rb_define_method(cPKCS7, "crls", ossl_pkcs7_get_crls, 0); rb_define_method(cPKCS7, "add_data", ossl_pkcs7_add_data, 1); rb_define_alias(cPKCS7, "data=", "add_data"); rb_define_method(cPKCS7, "verify", ossl_pkcs7_verify, -1); rb_define_method(cPKCS7, "decrypt", ossl_pkcs7_decrypt, -1); rb_define_method(cPKCS7, "to_pem", ossl_pkcs7_to_pem, 0); rb_define_alias(cPKCS7, "to_s", "to_pem"); rb_define_method(cPKCS7, "to_der", ossl_pkcs7_to_der, 0); cPKCS7Signer = rb_define_class_under(cPKCS7, "SignerInfo", rb_cObject); rb_define_const(cPKCS7, "Signer", cPKCS7Signer); rb_define_alloc_func(cPKCS7Signer, ossl_pkcs7si_alloc); rb_define_method(cPKCS7Signer, "initialize", ossl_pkcs7si_initialize,3); rb_define_method(cPKCS7Signer, "issuer", ossl_pkcs7si_get_issuer, 0); rb_define_alias(cPKCS7Signer, "name", "issuer"); rb_define_method(cPKCS7Signer, "serial", ossl_pkcs7si_get_serial,0); rb_define_method(cPKCS7Signer,"signed_time",ossl_pkcs7si_get_signed_time,0); cPKCS7Recipient = rb_define_class_under(cPKCS7,"RecipientInfo",rb_cObject); rb_define_alloc_func(cPKCS7Recipient, ossl_pkcs7ri_alloc); rb_define_method(cPKCS7Recipient, "initialize", ossl_pkcs7ri_initialize,1); rb_define_method(cPKCS7Recipient, "issuer", ossl_pkcs7ri_get_issuer,0); rb_define_method(cPKCS7Recipient, "serial", ossl_pkcs7ri_get_serial,0); rb_define_method(cPKCS7Recipient, "enc_key", ossl_pkcs7ri_get_enc_key,0); #define DefPKCS7Const(x) rb_define_const(cPKCS7, #x, INT2NUM(PKCS7_##x)) DefPKCS7Const(TEXT); DefPKCS7Const(NOCERTS); DefPKCS7Const(NOSIGS); DefPKCS7Const(NOCHAIN); DefPKCS7Const(NOINTERN); DefPKCS7Const(NOVERIFY); DefPKCS7Const(DETACHED); DefPKCS7Const(BINARY); DefPKCS7Const(NOATTR); DefPKCS7Const(NOSMIMECAP); } openssl-2.0.9/ext/openssl/ossl_pkcs7.h000066400000000000000000000006621336157045000177400ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_PKCS7_H_) #define _OSSL_PKCS7_H_ extern VALUE cPKCS7; extern VALUE cPKCS7Signer; extern VALUE cPKCS7Recipient; extern VALUE ePKCS7Error; void Init_ossl_pkcs7(void); #endif /* _OSSL_PKCS7_H_ */ openssl-2.0.9/ext/openssl/ossl_pkey.c000066400000000000000000000314761336157045000176630ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" /* * Classes */ VALUE mPKey; VALUE cPKey; VALUE ePKeyError; static ID id_private_q; /* * callback for generating keys */ static VALUE call_check_ints0(VALUE arg) { rb_thread_check_ints(); return Qnil; } static void * call_check_ints(void *arg) { int state; rb_protect(call_check_ints0, Qnil, &state); return (void *)(VALUE)state; } int ossl_generate_cb_2(int p, int n, BN_GENCB *cb) { VALUE ary; struct ossl_generate_cb_arg *arg; int state; arg = (struct ossl_generate_cb_arg *)BN_GENCB_get_arg(cb); if (arg->yield) { ary = rb_ary_new2(2); rb_ary_store(ary, 0, INT2NUM(p)); rb_ary_store(ary, 1, INT2NUM(n)); /* * can be break by raising exception or 'break' */ rb_protect(rb_yield, ary, &state); if (state) { arg->state = state; return 0; } } if (arg->interrupted) { arg->interrupted = 0; state = (int)(VALUE)rb_thread_call_with_gvl(call_check_ints, NULL); if (state) { arg->state = state; return 0; } } return 1; } void ossl_generate_cb_stop(void *ptr) { struct ossl_generate_cb_arg *arg = (struct ossl_generate_cb_arg *)ptr; arg->interrupted = 1; } static void ossl_evp_pkey_free(void *ptr) { EVP_PKEY_free(ptr); } /* * Public */ const rb_data_type_t ossl_evp_pkey_type = { "OpenSSL/EVP_PKEY", { 0, ossl_evp_pkey_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static VALUE pkey_new0(EVP_PKEY *pkey) { VALUE obj; int type; if (!pkey || (type = EVP_PKEY_base_id(pkey)) == EVP_PKEY_NONE) ossl_raise(rb_eRuntimeError, "pkey is empty"); switch (type) { #if !defined(OPENSSL_NO_RSA) case EVP_PKEY_RSA: return ossl_rsa_new(pkey); #endif #if !defined(OPENSSL_NO_DSA) case EVP_PKEY_DSA: return ossl_dsa_new(pkey); #endif #if !defined(OPENSSL_NO_DH) case EVP_PKEY_DH: return ossl_dh_new(pkey); #endif #if !defined(OPENSSL_NO_EC) && (OPENSSL_VERSION_NUMBER >= 0x0090802fL) case EVP_PKEY_EC: return ossl_ec_new(pkey); #endif default: obj = NewPKey(cPKey); SetPKey(obj, pkey); return obj; } } VALUE ossl_pkey_new(EVP_PKEY *pkey) { VALUE obj; int status; obj = rb_protect((VALUE (*)(VALUE))pkey_new0, (VALUE)pkey, &status); if (status) { EVP_PKEY_free(pkey); rb_jump_tag(status); } return obj; } /* * call-seq: * OpenSSL::PKey.read(string [, pwd ]) -> PKey * OpenSSL::PKey.read(io [, pwd ]) -> PKey * * Reads a DER or PEM encoded string from +string+ or +io+ and returns an * instance of the appropriate PKey class. * * === Parameters * * +string+ is a DER- or PEM-encoded string containing an arbitrary private * or public key. * * +io+ is an instance of +IO+ containing a DER- or PEM-encoded * arbitrary private or public key. * * +pwd+ is an optional password in case +string+ or +file+ is an encrypted * PEM resource. */ static VALUE ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; BIO *bio; VALUE data, pass; rb_scan_args(argc, argv, "11", &data, &pass); pass = ossl_pem_passwd_value(pass); bio = ossl_obj2bio(&data); if (!(pkey = d2i_PrivateKey_bio(bio, NULL))) { OSSL_BIO_reset(bio); if (!(pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, (void *)pass))) { OSSL_BIO_reset(bio); if (!(pkey = d2i_PUBKEY_bio(bio, NULL))) { OSSL_BIO_reset(bio); pkey = PEM_read_bio_PUBKEY(bio, NULL, ossl_pem_passwd_cb, (void *)pass); } } } BIO_free(bio); if (!pkey) ossl_raise(ePKeyError, "Could not parse PKey"); return ossl_pkey_new(pkey); } void ossl_pkey_check_public_key(const EVP_PKEY *pkey) { void *ptr; const BIGNUM *n, *e, *pubkey; if (EVP_PKEY_missing_parameters(pkey)) ossl_raise(ePKeyError, "parameters missing"); /* OpenSSL < 1.1.0 takes non-const pointer */ ptr = EVP_PKEY_get0((EVP_PKEY *)pkey); switch (EVP_PKEY_base_id(pkey)) { case EVP_PKEY_RSA: RSA_get0_key(ptr, &n, &e, NULL); if (n && e) return; break; case EVP_PKEY_DSA: DSA_get0_key(ptr, &pubkey, NULL); if (pubkey) return; break; case EVP_PKEY_DH: DH_get0_key(ptr, &pubkey, NULL); if (pubkey) return; break; #if !defined(OPENSSL_NO_EC) case EVP_PKEY_EC: if (EC_KEY_get0_public_key(ptr)) return; break; #endif default: /* unsupported type; assuming ok */ return; } ossl_raise(ePKeyError, "public key missing"); } EVP_PKEY * GetPKeyPtr(VALUE obj) { EVP_PKEY *pkey; SafeGetPKey(obj, pkey); return pkey; } EVP_PKEY * GetPrivPKeyPtr(VALUE obj) { EVP_PKEY *pkey; if (rb_funcallv(obj, id_private_q, 0, NULL) != Qtrue) { ossl_raise(rb_eArgError, "Private key is needed."); } SafeGetPKey(obj, pkey); return pkey; } EVP_PKEY * DupPKeyPtr(VALUE obj) { EVP_PKEY *pkey; SafeGetPKey(obj, pkey); EVP_PKEY_up_ref(pkey); return pkey; } /* * Private */ static VALUE ossl_pkey_alloc(VALUE klass) { EVP_PKEY *pkey; VALUE obj; obj = NewPKey(klass); if (!(pkey = EVP_PKEY_new())) { ossl_raise(ePKeyError, NULL); } SetPKey(obj, pkey); return obj; } /* * call-seq: * PKeyClass.new -> self * * Because PKey is an abstract class, actually calling this method explicitly * will raise a +NotImplementedError+. */ static VALUE ossl_pkey_initialize(VALUE self) { if (rb_obj_is_instance_of(self, cPKey)) { ossl_raise(rb_eTypeError, "OpenSSL::PKey::PKey can't be instantiated directly"); } return self; } /* * call-seq: * pkey.sign(digest, data) -> String * * To sign the +String+ +data+, +digest+, an instance of OpenSSL::Digest, must * be provided. The return value is again a +String+ containing the signature. * A PKeyError is raised should errors occur. * Any previous state of the +Digest+ instance is irrelevant to the signature * outcome, the digest instance is reset to its initial state during the * operation. * * == Example * data = 'Sign me!' * digest = OpenSSL::Digest::SHA256.new * pkey = OpenSSL::PKey::RSA.new(2048) * signature = pkey.sign(digest, data) */ static VALUE ossl_pkey_sign(VALUE self, VALUE digest, VALUE data) { EVP_PKEY *pkey; const EVP_MD *md; EVP_MD_CTX *ctx; unsigned int buf_len; VALUE str; int result; pkey = GetPrivPKeyPtr(self); md = GetDigestPtr(digest); StringValue(data); str = rb_str_new(0, EVP_PKEY_size(pkey)); ctx = EVP_MD_CTX_new(); if (!ctx) ossl_raise(ePKeyError, "EVP_MD_CTX_new"); if (!EVP_SignInit_ex(ctx, md, NULL)) { EVP_MD_CTX_free(ctx); ossl_raise(ePKeyError, "EVP_SignInit_ex"); } if (!EVP_SignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) { EVP_MD_CTX_free(ctx); ossl_raise(ePKeyError, "EVP_SignUpdate"); } result = EVP_SignFinal(ctx, (unsigned char *)RSTRING_PTR(str), &buf_len, pkey); EVP_MD_CTX_free(ctx); if (!result) ossl_raise(ePKeyError, "EVP_SignFinal"); rb_str_set_len(str, buf_len); return str; } /* * call-seq: * pkey.verify(digest, signature, data) -> String * * To verify the +String+ +signature+, +digest+, an instance of * OpenSSL::Digest, must be provided to re-compute the message digest of the * original +data+, also a +String+. The return value is +true+ if the * signature is valid, +false+ otherwise. A PKeyError is raised should errors * occur. * Any previous state of the +Digest+ instance is irrelevant to the validation * outcome, the digest instance is reset to its initial state during the * operation. * * == Example * data = 'Sign me!' * digest = OpenSSL::Digest::SHA256.new * pkey = OpenSSL::PKey::RSA.new(2048) * signature = pkey.sign(digest, data) * pub_key = pkey.public_key * puts pub_key.verify(digest, signature, data) # => true */ static VALUE ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data) { EVP_PKEY *pkey; const EVP_MD *md; EVP_MD_CTX *ctx; int siglen, result; GetPKey(self, pkey); ossl_pkey_check_public_key(pkey); md = GetDigestPtr(digest); StringValue(sig); siglen = RSTRING_LENINT(sig); StringValue(data); ctx = EVP_MD_CTX_new(); if (!ctx) ossl_raise(ePKeyError, "EVP_MD_CTX_new"); if (!EVP_VerifyInit_ex(ctx, md, NULL)) { EVP_MD_CTX_free(ctx); ossl_raise(ePKeyError, "EVP_VerifyInit_ex"); } if (!EVP_VerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) { EVP_MD_CTX_free(ctx); ossl_raise(ePKeyError, "EVP_VerifyUpdate"); } result = EVP_VerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), siglen, pkey); EVP_MD_CTX_free(ctx); switch (result) { case 0: ossl_clear_error(); return Qfalse; case 1: return Qtrue; default: ossl_raise(ePKeyError, "EVP_VerifyFinal"); } } /* * INIT */ void Init_ossl_pkey(void) { #undef rb_intern #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif /* Document-module: OpenSSL::PKey * * == Asymmetric Public Key Algorithms * * Asymmetric public key algorithms solve the problem of establishing and * sharing secret keys to en-/decrypt messages. The key in such an * algorithm consists of two parts: a public key that may be distributed * to others and a private key that needs to remain secret. * * Messages encrypted with a public key can only be decrypted by * recipients that are in possession of the associated private key. * Since public key algorithms are considerably slower than symmetric * key algorithms (cf. OpenSSL::Cipher) they are often used to establish * a symmetric key shared between two parties that are in possession of * each other's public key. * * Asymmetric algorithms offer a lot of nice features that are used in a * lot of different areas. A very common application is the creation and * validation of digital signatures. To sign a document, the signatory * generally uses a message digest algorithm (cf. OpenSSL::Digest) to * compute a digest of the document that is then encrypted (i.e. signed) * using the private key. Anyone in possession of the public key may then * verify the signature by computing the message digest of the original * document on their own, decrypting the signature using the signatory's * public key and comparing the result to the message digest they * previously computed. The signature is valid if and only if the * decrypted signature is equal to this message digest. * * The PKey module offers support for three popular public/private key * algorithms: * * RSA (OpenSSL::PKey::RSA) * * DSA (OpenSSL::PKey::DSA) * * Elliptic Curve Cryptography (OpenSSL::PKey::EC) * Each of these implementations is in fact a sub-class of the abstract * PKey class which offers the interface for supporting digital signatures * in the form of PKey#sign and PKey#verify. * * == Diffie-Hellman Key Exchange * * Finally PKey also features OpenSSL::PKey::DH, an implementation of * the Diffie-Hellman key exchange protocol based on discrete logarithms * in finite fields, the same basis that DSA is built on. * The Diffie-Hellman protocol can be used to exchange (symmetric) keys * over insecure channels without needing any prior joint knowledge * between the participating parties. As the security of DH demands * relatively long "public keys" (i.e. the part that is overtly * transmitted between participants) DH tends to be quite slow. If * security or speed is your primary concern, OpenSSL::PKey::EC offers * another implementation of the Diffie-Hellman protocol. * */ mPKey = rb_define_module_under(mOSSL, "PKey"); /* Document-class: OpenSSL::PKey::PKeyError * *Raised when errors occur during PKey#sign or PKey#verify. */ ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); /* Document-class: OpenSSL::PKey::PKey * * An abstract class that bundles signature creation (PKey#sign) and * validation (PKey#verify) that is common to all implementations except * OpenSSL::PKey::DH * * OpenSSL::PKey::RSA * * OpenSSL::PKey::DSA * * OpenSSL::PKey::EC */ cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); rb_define_module_function(mPKey, "read", ossl_pkey_new_from_data, -1); rb_define_alloc_func(cPKey, ossl_pkey_alloc); rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0); rb_define_method(cPKey, "sign", ossl_pkey_sign, 2); rb_define_method(cPKey, "verify", ossl_pkey_verify, 3); id_private_q = rb_intern("private?"); /* * INIT rsa, dsa, dh, ec */ Init_ossl_rsa(); Init_ossl_dsa(); Init_ossl_dh(); Init_ossl_ec(); } openssl-2.0.9/ext/openssl/ossl_pkey.h000066400000000000000000000163521336157045000176640ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_PKEY_H_) #define _OSSL_PKEY_H_ extern VALUE mPKey; extern VALUE cPKey; extern VALUE ePKeyError; extern const rb_data_type_t ossl_evp_pkey_type; #define OSSL_PKEY_SET_PRIVATE(obj) rb_iv_set((obj), "private", Qtrue) #define OSSL_PKEY_SET_PUBLIC(obj) rb_iv_set((obj), "private", Qfalse) #define OSSL_PKEY_IS_PRIVATE(obj) (rb_iv_get((obj), "private") == Qtrue) #define NewPKey(klass) \ TypedData_Wrap_Struct((klass), &ossl_evp_pkey_type, 0) #define SetPKey(obj, pkey) do { \ if (!(pkey)) { \ rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (pkey); \ OSSL_PKEY_SET_PUBLIC(obj); \ } while (0) #define GetPKey(obj, pkey) do {\ TypedData_Get_Struct((obj), EVP_PKEY, &ossl_evp_pkey_type, (pkey)); \ if (!(pkey)) { \ rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!");\ } \ } while (0) #define SafeGetPKey(obj, pkey) do { \ OSSL_Check_Kind((obj), cPKey); \ GetPKey((obj), (pkey)); \ } while (0) struct ossl_generate_cb_arg { int yield; int interrupted; int state; }; int ossl_generate_cb_2(int p, int n, BN_GENCB *cb); void ossl_generate_cb_stop(void *ptr); VALUE ossl_pkey_new(EVP_PKEY *); void ossl_pkey_check_public_key(const EVP_PKEY *); EVP_PKEY *GetPKeyPtr(VALUE); EVP_PKEY *DupPKeyPtr(VALUE); EVP_PKEY *GetPrivPKeyPtr(VALUE); void Init_ossl_pkey(void); /* * RSA */ extern VALUE cRSA; extern VALUE eRSAError; VALUE ossl_rsa_new(EVP_PKEY *); void Init_ossl_rsa(void); /* * DSA */ extern VALUE cDSA; extern VALUE eDSAError; VALUE ossl_dsa_new(EVP_PKEY *); void Init_ossl_dsa(void); /* * DH */ extern VALUE cDH; extern VALUE eDHError; VALUE ossl_dh_new(EVP_PKEY *); void Init_ossl_dh(void); /* * EC */ extern VALUE cEC; extern VALUE eECError; extern VALUE cEC_GROUP; extern VALUE eEC_GROUP; extern VALUE cEC_POINT; extern VALUE eEC_POINT; VALUE ossl_ec_new(EVP_PKEY *); void Init_ossl_ec(void); #define OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, _name, _get) \ /* \ * call-seq: \ * _keytype##.##_name -> aBN \ */ \ static VALUE ossl_##_keytype##_get_##_name(VALUE self) \ { \ _type *obj; \ const BIGNUM *bn; \ \ Get##_type(self, obj); \ _get; \ if (bn == NULL) \ return Qnil; \ return ossl_bn_new(bn); \ } #define OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3) \ OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a1, \ _type##_get0_##_group(obj, &bn, NULL, NULL)) \ OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a2, \ _type##_get0_##_group(obj, NULL, &bn, NULL)) \ OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a3, \ _type##_get0_##_group(obj, NULL, NULL, &bn)) #define OSSL_PKEY_BN_DEF_GETTER2(_keytype, _type, _group, a1, a2) \ OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a1, \ _type##_get0_##_group(obj, &bn, NULL)) \ OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a2, \ _type##_get0_##_group(obj, NULL, &bn)) #define OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) \ /* \ * call-seq: \ * _keytype##.set_##_group(a1, a2, a3) -> self \ */ \ static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2, VALUE v3) \ { \ _type *obj; \ BIGNUM *bn1 = NULL, *orig_bn1 = NIL_P(v1) ? NULL : GetBNPtr(v1);\ BIGNUM *bn2 = NULL, *orig_bn2 = NIL_P(v2) ? NULL : GetBNPtr(v2);\ BIGNUM *bn3 = NULL, *orig_bn3 = NIL_P(v3) ? NULL : GetBNPtr(v3);\ \ Get##_type(self, obj); \ if (orig_bn1 && !(bn1 = BN_dup(orig_bn1)) || \ orig_bn2 && !(bn2 = BN_dup(orig_bn2)) || \ orig_bn3 && !(bn3 = BN_dup(orig_bn3))) { \ BN_clear_free(bn1); \ BN_clear_free(bn2); \ BN_clear_free(bn3); \ ossl_raise(eBNError, NULL); \ } \ \ if (!_type##_set0_##_group(obj, bn1, bn2, bn3)) { \ BN_clear_free(bn1); \ BN_clear_free(bn2); \ BN_clear_free(bn3); \ ossl_raise(ePKeyError, #_type"_set0_"#_group); \ } \ return self; \ } #define OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) \ /* \ * call-seq: \ * _keytype##.set_##_group(a1, a2) -> self \ */ \ static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2) \ { \ _type *obj; \ BIGNUM *bn1 = NULL, *orig_bn1 = NIL_P(v1) ? NULL : GetBNPtr(v1);\ BIGNUM *bn2 = NULL, *orig_bn2 = NIL_P(v2) ? NULL : GetBNPtr(v2);\ \ Get##_type(self, obj); \ if (orig_bn1 && !(bn1 = BN_dup(orig_bn1)) || \ orig_bn2 && !(bn2 = BN_dup(orig_bn2))) { \ BN_clear_free(bn1); \ BN_clear_free(bn2); \ ossl_raise(eBNError, NULL); \ } \ \ if (!_type##_set0_##_group(obj, bn1, bn2)) { \ BN_clear_free(bn1); \ BN_clear_free(bn2); \ ossl_raise(ePKeyError, #_type"_set0_"#_group); \ } \ return self; \ } #define OSSL_PKEY_BN_DEF_SETTER_OLD(_keytype, _type, _group, _name) \ /* \ * call-seq: \ * _keytype##.##_name = bn -> bn \ */ \ static VALUE ossl_##_keytype##_set_##_name(VALUE self, VALUE bignum) \ { \ _type *obj; \ BIGNUM *bn; \ \ rb_warning("#"#_name"= is deprecated; use #set_"#_group); \ Get##_type(self, obj); \ if (NIL_P(bignum)) { \ BN_clear_free(obj->_name); \ obj->_name = NULL; \ return Qnil; \ } \ \ bn = GetBNPtr(bignum); \ if (obj->_name == NULL) \ obj->_name = BN_new(); \ if (obj->_name == NULL) \ ossl_raise(eBNError, NULL); \ if (BN_copy(obj->_name, bn) == NULL) \ ossl_raise(eBNError, NULL); \ return bignum; \ } #if defined(HAVE_OPAQUE_OPENSSL) /* OpenSSL 1.1.0 */ #define OSSL_PKEY_BN_DEF3(_keytype, _type, _group, a1, a2, a3) \ OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3) \ OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) #define OSSL_PKEY_BN_DEF2(_keytype, _type, _group, a1, a2) \ OSSL_PKEY_BN_DEF_GETTER2(_keytype, _type, _group, a1, a2) \ OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) #define DEF_OSSL_PKEY_BN(class, keytype, name) \ rb_define_method((class), #name, ossl_##keytype##_get_##name, 0) #else #define OSSL_PKEY_BN_DEF3(_keytype, _type, _group, a1, a2, a3) \ OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3) \ OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3) \ OSSL_PKEY_BN_DEF_SETTER_OLD(_keytype, _type, _group, a1) \ OSSL_PKEY_BN_DEF_SETTER_OLD(_keytype, _type, _group, a2) \ OSSL_PKEY_BN_DEF_SETTER_OLD(_keytype, _type, _group, a3) #define OSSL_PKEY_BN_DEF2(_keytype, _type, _group, a1, a2) \ OSSL_PKEY_BN_DEF_GETTER2(_keytype, _type, _group, a1, a2) \ OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2) \ OSSL_PKEY_BN_DEF_SETTER_OLD(_keytype, _type, _group, a1) \ OSSL_PKEY_BN_DEF_SETTER_OLD(_keytype, _type, _group, a2) #define DEF_OSSL_PKEY_BN(class, keytype, name) do { \ rb_define_method((class), #name, ossl_##keytype##_get_##name, 0);\ rb_define_method((class), #name "=", ossl_##keytype##_set_##name, 1);\ } while (0) #endif /* HAVE_OPAQUE_OPENSSL */ #endif /* _OSSL_PKEY_H_ */ openssl-2.0.9/ext/openssl/ossl_pkey_dh.c000066400000000000000000000373501336157045000203330ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #if !defined(OPENSSL_NO_DH) #define GetPKeyDH(obj, pkey) do { \ GetPKey((obj), (pkey)); \ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) { /* PARANOIA? */ \ ossl_raise(rb_eRuntimeError, "THIS IS NOT A DH!") ; \ } \ } while (0) #define GetDH(obj, dh) do { \ EVP_PKEY *_pkey; \ GetPKeyDH((obj), _pkey); \ (dh) = EVP_PKEY_get0_DH(_pkey); \ } while (0) /* * Classes */ VALUE cDH; VALUE eDHError; /* * Public */ static VALUE dh_instance(VALUE klass, DH *dh) { EVP_PKEY *pkey; VALUE obj; if (!dh) { return Qfalse; } obj = NewPKey(klass); if (!(pkey = EVP_PKEY_new())) { return Qfalse; } if (!EVP_PKEY_assign_DH(pkey, dh)) { EVP_PKEY_free(pkey); return Qfalse; } SetPKey(obj, pkey); return obj; } VALUE ossl_dh_new(EVP_PKEY *pkey) { VALUE obj; if (!pkey) { obj = dh_instance(cDH, DH_new()); } else { obj = NewPKey(cDH); if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) { ossl_raise(rb_eTypeError, "Not a DH key!"); } SetPKey(obj, pkey); } if (obj == Qfalse) { ossl_raise(eDHError, NULL); } return obj; } /* * Private */ struct dh_blocking_gen_arg { DH *dh; int size; int gen; BN_GENCB *cb; int result; }; static void * dh_blocking_gen(void *arg) { struct dh_blocking_gen_arg *gen = (struct dh_blocking_gen_arg *)arg; gen->result = DH_generate_parameters_ex(gen->dh, gen->size, gen->gen, gen->cb); return 0; } static DH * dh_generate(int size, int gen) { struct ossl_generate_cb_arg cb_arg = { 0 }; struct dh_blocking_gen_arg gen_arg; DH *dh = DH_new(); BN_GENCB *cb = BN_GENCB_new(); if (!dh || !cb) { DH_free(dh); BN_GENCB_free(cb); return NULL; } if (rb_block_given_p()) cb_arg.yield = 1; BN_GENCB_set(cb, ossl_generate_cb_2, &cb_arg); gen_arg.dh = dh; gen_arg.size = size; gen_arg.gen = gen; gen_arg.cb = cb; if (cb_arg.yield == 1) { /* we cannot release GVL when callback proc is supplied */ dh_blocking_gen(&gen_arg); } else { /* there's a chance to unblock */ rb_thread_call_without_gvl(dh_blocking_gen, &gen_arg, ossl_generate_cb_stop, &cb_arg); } BN_GENCB_free(cb); if (!gen_arg.result) { DH_free(dh); if (cb_arg.state) { /* Clear OpenSSL error queue before re-raising. */ ossl_clear_error(); rb_jump_tag(cb_arg.state); } return NULL; } if (!DH_generate_key(dh)) { DH_free(dh); return NULL; } return dh; } /* * call-seq: * DH.generate(size [, generator]) -> dh * * Creates a new DH instance from scratch by generating the private and public * components alike. * * === Parameters * * +size+ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure. * * +generator+ is a small number > 1, typically 2 or 5. * */ static VALUE ossl_dh_s_generate(int argc, VALUE *argv, VALUE klass) { DH *dh ; int g = 2; VALUE size, gen, obj; if (rb_scan_args(argc, argv, "11", &size, &gen) == 2) { g = NUM2INT(gen); } dh = dh_generate(NUM2INT(size), g); obj = dh_instance(klass, dh); if (obj == Qfalse) { DH_free(dh); ossl_raise(eDHError, NULL); } return obj; } /* * call-seq: * DH.new -> dh * DH.new(string) -> dh * DH.new(size [, generator]) -> dh * * Either generates a DH instance from scratch or by reading already existing * DH parameters from +string+. Note that when reading a DH instance from * data that was encoded from a DH instance by using DH#to_pem or DH#to_der * the result will *not* contain a public/private key pair yet. This needs to * be generated using DH#generate_key! first. * * === Parameters * * +size+ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure. * * +generator+ is a small number > 1, typically 2 or 5. * * +string+ contains the DER or PEM encoded key. * * === Examples * DH.new # -> dh * DH.new(1024) # -> dh * DH.new(1024, 5) # -> dh * #Reading DH parameters * dh = DH.new(File.read('parameters.pem')) # -> dh, but no public/private key yet * dh.generate_key! # -> dh with public and private key */ static VALUE ossl_dh_initialize(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; DH *dh; int g = 2; BIO *in; VALUE arg, gen; GetPKey(self, pkey); if(rb_scan_args(argc, argv, "02", &arg, &gen) == 0) { dh = DH_new(); } else if (RB_INTEGER_TYPE_P(arg)) { if (!NIL_P(gen)) { g = NUM2INT(gen); } if (!(dh = dh_generate(NUM2INT(arg), g))) { ossl_raise(eDHError, NULL); } } else { arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL); if (!dh){ OSSL_BIO_reset(in); dh = d2i_DHparams_bio(in, NULL); } BIO_free(in); if (!dh) { ossl_raise(eDHError, NULL); } } if (!EVP_PKEY_assign_DH(pkey, dh)) { DH_free(dh); ossl_raise(eDHError, NULL); } return self; } static VALUE ossl_dh_initialize_copy(VALUE self, VALUE other) { EVP_PKEY *pkey; DH *dh, *dh_other; const BIGNUM *pub, *priv; GetPKey(self, pkey); if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE) ossl_raise(eDHError, "DH already initialized"); GetDH(other, dh_other); dh = DHparams_dup(dh_other); if (!dh) ossl_raise(eDHError, "DHparams_dup"); EVP_PKEY_assign_DH(pkey, dh); DH_get0_key(dh_other, &pub, &priv); if (pub) { BIGNUM *pub2 = BN_dup(pub); BIGNUM *priv2 = BN_dup(priv); if (!pub2 || priv && !priv2) { BN_clear_free(pub2); BN_clear_free(priv2); ossl_raise(eDHError, "BN_dup"); } DH_set0_key(dh, pub2, priv2); } return self; } /* * call-seq: * dh.public? -> true | false * * Indicates whether this DH instance has a public key associated with it or * not. The public key may be retrieved with DH#pub_key. */ static VALUE ossl_dh_is_public(VALUE self) { DH *dh; const BIGNUM *bn; GetDH(self, dh); DH_get0_key(dh, &bn, NULL); return bn ? Qtrue : Qfalse; } /* * call-seq: * dh.private? -> true | false * * Indicates whether this DH instance has a private key associated with it or * not. The private key may be retrieved with DH#priv_key. */ static VALUE ossl_dh_is_private(VALUE self) { DH *dh; const BIGNUM *bn; GetDH(self, dh); DH_get0_key(dh, NULL, &bn); #if !defined(OPENSSL_NO_ENGINE) return (bn || DH_get0_engine(dh)) ? Qtrue : Qfalse; #else return bn ? Qtrue : Qfalse; #endif } /* * call-seq: * dh.export -> aString * dh.to_pem -> aString * dh.to_s -> aString * * Encodes this DH to its PEM encoding. Note that any existing per-session * public/private keys will *not* get encoded, just the Diffie-Hellman * parameters will be encoded. */ static VALUE ossl_dh_export(VALUE self) { DH *dh; BIO *out; VALUE str; GetDH(self, dh); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eDHError, NULL); } if (!PEM_write_bio_DHparams(out, dh)) { BIO_free(out); ossl_raise(eDHError, NULL); } str = ossl_membio2str(out); return str; } /* * call-seq: * dh.to_der -> aString * * Encodes this DH to its DER encoding. Note that any existing per-session * public/private keys will *not* get encoded, just the Diffie-Hellman * parameters will be encoded. */ static VALUE ossl_dh_to_der(VALUE self) { DH *dh; unsigned char *p; long len; VALUE str; GetDH(self, dh); if((len = i2d_DHparams(dh, NULL)) <= 0) ossl_raise(eDHError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_DHparams(dh, &p) < 0) ossl_raise(eDHError, NULL); ossl_str_adjust(str, p); return str; } /* * call-seq: * dh.params -> hash * * Stores all parameters of key to the hash * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! * Don't use :-)) (I's up to you) */ static VALUE ossl_dh_get_params(VALUE self) { DH *dh; VALUE hash; const BIGNUM *p, *q, *g, *pub_key, *priv_key; GetDH(self, dh); DH_get0_pqg(dh, &p, &q, &g); DH_get0_key(dh, &pub_key, &priv_key); hash = rb_hash_new(); rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(p)); rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(q)); rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(g)); rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pub_key)); rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(priv_key)); return hash; } /* * call-seq: * dh.to_text -> aString * * Prints all parameters of key to buffer * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! * Don't use :-)) (I's up to you) */ static VALUE ossl_dh_to_text(VALUE self) { DH *dh; BIO *out; VALUE str; GetDH(self, dh); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eDHError, NULL); } if (!DHparams_print(out, dh)) { BIO_free(out); ossl_raise(eDHError, NULL); } str = ossl_membio2str(out); return str; } /* * call-seq: * dh.public_key -> aDH * * Returns a new DH instance that carries just the public information, i.e. * the prime +p+ and the generator +g+, but no public/private key yet. Such * a pair may be generated using DH#generate_key!. The "public key" needed * for a key exchange with DH#compute_key is considered as per-session * information and may be retrieved with DH#pub_key once a key pair has * been generated. * If the current instance already contains private information (and thus a * valid public/private key pair), this information will no longer be present * in the new instance generated by DH#public_key. This feature is helpful for * publishing the Diffie-Hellman parameters without leaking any of the private * per-session information. * * === Example * dh = OpenSSL::PKey::DH.new(2048) # has public and private key set * public_key = dh.public_key # contains only prime and generator * parameters = public_key.to_der # it's safe to publish this */ static VALUE ossl_dh_to_public_key(VALUE self) { DH *orig_dh, *dh; VALUE obj; GetDH(self, orig_dh); dh = DHparams_dup(orig_dh); /* err check perfomed by dh_instance */ obj = dh_instance(rb_obj_class(self), dh); if (obj == Qfalse) { DH_free(dh); ossl_raise(eDHError, NULL); } return obj; } /* * call-seq: * dh.params_ok? -> true | false * * Validates the Diffie-Hellman parameters associated with this instance. * It checks whether a safe prime and a suitable generator are used. If this * is not the case, +false+ is returned. */ static VALUE ossl_dh_check_params(VALUE self) { DH *dh; int codes; GetDH(self, dh); if (!DH_check(dh, &codes)) { return Qfalse; } return codes == 0 ? Qtrue : Qfalse; } /* * call-seq: * dh.generate_key! -> self * * Generates a private and public key unless a private key already exists. * If this DH instance was generated from public DH parameters (e.g. by * encoding the result of DH#public_key), then this method needs to be * called first in order to generate the per-session keys before performing * the actual key exchange. * * === Example * dh = OpenSSL::PKey::DH.new(2048) * public_key = dh.public_key #contains no private/public key yet * public_key.generate_key! * puts public_key.private? # => true */ static VALUE ossl_dh_generate_key(VALUE self) { DH *dh; GetDH(self, dh); if (!DH_generate_key(dh)) ossl_raise(eDHError, "Failed to generate key"); return self; } /* * call-seq: * dh.compute_key(pub_bn) -> aString * * Returns a String containing a shared secret computed from the other party's public value. * See DH_compute_key() for further information. * * === Parameters * * +pub_bn+ is a OpenSSL::BN, *not* the DH instance returned by * DH#public_key as that contains the DH parameters only. */ static VALUE ossl_dh_compute_key(VALUE self, VALUE pub) { DH *dh; const BIGNUM *pub_key, *dh_p; VALUE str; int len; GetDH(self, dh); DH_get0_pqg(dh, &dh_p, NULL, NULL); if (!dh_p) ossl_raise(eDHError, "incomplete DH"); pub_key = GetBNPtr(pub); len = DH_size(dh); str = rb_str_new(0, len); if ((len = DH_compute_key((unsigned char *)RSTRING_PTR(str), pub_key, dh)) < 0) { ossl_raise(eDHError, NULL); } rb_str_set_len(str, len); return str; } /* * Document-method: OpenSSL::PKey::DH#set_pqg * call-seq: * dh.set_pqg(p, q, g) -> self * * Sets +p+, +q+, +g+ for the DH instance. */ OSSL_PKEY_BN_DEF3(dh, DH, pqg, p, q, g) /* * Document-method: OpenSSL::PKey::DH#set_key * call-seq: * dh.set_key(pub_key, priv_key) -> self * * Sets +pub_key+ and +priv_key+ for the DH instance. +priv_key+ may be nil. */ OSSL_PKEY_BN_DEF2(dh, DH, key, pub_key, priv_key) /* * INIT */ void Init_ossl_dh(void) { #if 0 mPKey = rb_define_module_under(mOSSL, "PKey"); cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); #endif /* Document-class: OpenSSL::PKey::DHError * * Generic exception that is raised if an operation on a DH PKey * fails unexpectedly or in case an instantiation of an instance of DH * fails due to non-conformant input data. */ eDHError = rb_define_class_under(mPKey, "DHError", ePKeyError); /* Document-class: OpenSSL::PKey::DH * * An implementation of the Diffie-Hellman key exchange protocol based on * discrete logarithms in finite fields, the same basis that DSA is built * on. * * === Accessor methods for the Diffie-Hellman parameters * DH#p:: * The prime (an OpenSSL::BN) of the Diffie-Hellman parameters. * DH#g:: * The generator (an OpenSSL::BN) g of the Diffie-Hellman parameters. * DH#pub_key:: * The per-session public key (an OpenSSL::BN) matching the private key. * This needs to be passed to DH#compute_key. * DH#priv_key:: * The per-session private key, an OpenSSL::BN. * * === Example of a key exchange * dh1 = OpenSSL::PKey::DH.new(2048) * der = dh1.public_key.to_der #you may send this publicly to the participating party * dh2 = OpenSSL::PKey::DH.new(der) * dh2.generate_key! #generate the per-session key pair * symm_key1 = dh1.compute_key(dh2.pub_key) * symm_key2 = dh2.compute_key(dh1.pub_key) * * puts symm_key1 == symm_key2 # => true */ cDH = rb_define_class_under(mPKey, "DH", cPKey); rb_define_singleton_method(cDH, "generate", ossl_dh_s_generate, -1); rb_define_method(cDH, "initialize", ossl_dh_initialize, -1); rb_define_copy_func(cDH, ossl_dh_initialize_copy); rb_define_method(cDH, "public?", ossl_dh_is_public, 0); rb_define_method(cDH, "private?", ossl_dh_is_private, 0); rb_define_method(cDH, "to_text", ossl_dh_to_text, 0); rb_define_method(cDH, "export", ossl_dh_export, 0); rb_define_alias(cDH, "to_pem", "export"); rb_define_alias(cDH, "to_s", "export"); rb_define_method(cDH, "to_der", ossl_dh_to_der, 0); rb_define_method(cDH, "public_key", ossl_dh_to_public_key, 0); rb_define_method(cDH, "params_ok?", ossl_dh_check_params, 0); rb_define_method(cDH, "generate_key!", ossl_dh_generate_key, 0); rb_define_method(cDH, "compute_key", ossl_dh_compute_key, 1); DEF_OSSL_PKEY_BN(cDH, dh, p); DEF_OSSL_PKEY_BN(cDH, dh, q); DEF_OSSL_PKEY_BN(cDH, dh, g); DEF_OSSL_PKEY_BN(cDH, dh, pub_key); DEF_OSSL_PKEY_BN(cDH, dh, priv_key); rb_define_method(cDH, "set_pqg", ossl_dh_set_pqg, 3); rb_define_method(cDH, "set_key", ossl_dh_set_key, 2); rb_define_method(cDH, "params", ossl_dh_get_params, 0); } #else /* defined NO_DH */ void Init_ossl_dh(void) { } #endif /* NO_DH */ openssl-2.0.9/ext/openssl/ossl_pkey_dsa.c000066400000000000000000000377511336157045000205140ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #if !defined(OPENSSL_NO_DSA) #define GetPKeyDSA(obj, pkey) do { \ GetPKey((obj), (pkey)); \ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DSA) { /* PARANOIA? */ \ ossl_raise(rb_eRuntimeError, "THIS IS NOT A DSA!"); \ } \ } while (0) #define GetDSA(obj, dsa) do { \ EVP_PKEY *_pkey; \ GetPKeyDSA((obj), _pkey); \ (dsa) = EVP_PKEY_get0_DSA(_pkey); \ } while (0) static inline int DSA_HAS_PRIVATE(DSA *dsa) { const BIGNUM *bn; DSA_get0_key(dsa, NULL, &bn); return !!bn; } static inline int DSA_PRIVATE(VALUE obj, DSA *dsa) { return DSA_HAS_PRIVATE(dsa) || OSSL_PKEY_IS_PRIVATE(obj); } /* * Classes */ VALUE cDSA; VALUE eDSAError; /* * Public */ static VALUE dsa_instance(VALUE klass, DSA *dsa) { EVP_PKEY *pkey; VALUE obj; if (!dsa) { return Qfalse; } obj = NewPKey(klass); if (!(pkey = EVP_PKEY_new())) { return Qfalse; } if (!EVP_PKEY_assign_DSA(pkey, dsa)) { EVP_PKEY_free(pkey); return Qfalse; } SetPKey(obj, pkey); return obj; } VALUE ossl_dsa_new(EVP_PKEY *pkey) { VALUE obj; if (!pkey) { obj = dsa_instance(cDSA, DSA_new()); } else { obj = NewPKey(cDSA); if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DSA) { ossl_raise(rb_eTypeError, "Not a DSA key!"); } SetPKey(obj, pkey); } if (obj == Qfalse) { ossl_raise(eDSAError, NULL); } return obj; } /* * Private */ struct dsa_blocking_gen_arg { DSA *dsa; int size; int *counter; unsigned long *h; BN_GENCB *cb; int result; }; static void * dsa_blocking_gen(void *arg) { struct dsa_blocking_gen_arg *gen = (struct dsa_blocking_gen_arg *)arg; gen->result = DSA_generate_parameters_ex(gen->dsa, gen->size, NULL, 0, gen->counter, gen->h, gen->cb); return 0; } static DSA * dsa_generate(int size) { struct ossl_generate_cb_arg cb_arg = { 0 }; struct dsa_blocking_gen_arg gen_arg; DSA *dsa = DSA_new(); BN_GENCB *cb = BN_GENCB_new(); int counter; unsigned long h; if (!dsa || !cb) { DSA_free(dsa); BN_GENCB_free(cb); return NULL; } if (rb_block_given_p()) cb_arg.yield = 1; BN_GENCB_set(cb, ossl_generate_cb_2, &cb_arg); gen_arg.dsa = dsa; gen_arg.size = size; gen_arg.counter = &counter; gen_arg.h = &h; gen_arg.cb = cb; if (cb_arg.yield == 1) { /* we cannot release GVL when callback proc is supplied */ dsa_blocking_gen(&gen_arg); } else { /* there's a chance to unblock */ rb_thread_call_without_gvl(dsa_blocking_gen, &gen_arg, ossl_generate_cb_stop, &cb_arg); } BN_GENCB_free(cb); if (!gen_arg.result) { DSA_free(dsa); if (cb_arg.state) { /* Clear OpenSSL error queue before re-raising. By the way, the * documentation of DSA_generate_parameters_ex() says the error code * can be obtained by ERR_get_error(), but the default * implementation, dsa_builtin_paramgen() doesn't put any error... */ ossl_clear_error(); rb_jump_tag(cb_arg.state); } return NULL; } if (!DSA_generate_key(dsa)) { DSA_free(dsa); return NULL; } return dsa; } /* * call-seq: * DSA.generate(size) -> dsa * * Creates a new DSA instance by generating a private/public key pair * from scratch. * * === Parameters * * +size+ is an integer representing the desired key size. * */ static VALUE ossl_dsa_s_generate(VALUE klass, VALUE size) { DSA *dsa = dsa_generate(NUM2INT(size)); /* err handled by dsa_instance */ VALUE obj = dsa_instance(klass, dsa); if (obj == Qfalse) { DSA_free(dsa); ossl_raise(eDSAError, NULL); } return obj; } /* * call-seq: * DSA.new -> dsa * DSA.new(size) -> dsa * DSA.new(string [, pass]) -> dsa * * Creates a new DSA instance by reading an existing key from +string+. * * === Parameters * * +size+ is an integer representing the desired key size. * * +string+ contains a DER or PEM encoded key. * * +pass+ is a string that contains an optional password. * * === Examples * DSA.new -> dsa * DSA.new(1024) -> dsa * DSA.new(File.read('dsa.pem')) -> dsa * DSA.new(File.read('dsa.pem'), 'mypassword') -> dsa * */ static VALUE ossl_dsa_initialize(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; DSA *dsa; BIO *in; VALUE arg, pass; GetPKey(self, pkey); if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) { dsa = DSA_new(); } else if (RB_INTEGER_TYPE_P(arg)) { if (!(dsa = dsa_generate(NUM2INT(arg)))) { ossl_raise(eDSAError, NULL); } } else { pass = ossl_pem_passwd_value(pass); arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); dsa = PEM_read_bio_DSAPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass); if (!dsa) { OSSL_BIO_reset(in); dsa = PEM_read_bio_DSA_PUBKEY(in, NULL, NULL, NULL); } if (!dsa) { OSSL_BIO_reset(in); dsa = d2i_DSAPrivateKey_bio(in, NULL); } if (!dsa) { OSSL_BIO_reset(in); dsa = d2i_DSA_PUBKEY_bio(in, NULL); } if (!dsa) { OSSL_BIO_reset(in); #define PEM_read_bio_DSAPublicKey(bp,x,cb,u) (DSA *)PEM_ASN1_read_bio( \ (d2i_of_void *)d2i_DSAPublicKey, PEM_STRING_DSA_PUBLIC, (bp), (void **)(x), (cb), (u)) dsa = PEM_read_bio_DSAPublicKey(in, NULL, NULL, NULL); #undef PEM_read_bio_DSAPublicKey } BIO_free(in); if (!dsa) { ossl_clear_error(); ossl_raise(eDSAError, "Neither PUB key nor PRIV key"); } } if (!EVP_PKEY_assign_DSA(pkey, dsa)) { DSA_free(dsa); ossl_raise(eDSAError, NULL); } return self; } static VALUE ossl_dsa_initialize_copy(VALUE self, VALUE other) { EVP_PKEY *pkey; DSA *dsa, *dsa_new; GetPKey(self, pkey); if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE) ossl_raise(eDSAError, "DSA already initialized"); GetDSA(other, dsa); dsa_new = ASN1_dup((i2d_of_void *)i2d_DSAPrivateKey, (d2i_of_void *)d2i_DSAPrivateKey, (char *)dsa); if (!dsa_new) ossl_raise(eDSAError, "ASN1_dup"); EVP_PKEY_assign_DSA(pkey, dsa_new); return self; } /* * call-seq: * dsa.public? -> true | false * * Indicates whether this DSA instance has a public key associated with it or * not. The public key may be retrieved with DSA#public_key. */ static VALUE ossl_dsa_is_public(VALUE self) { DSA *dsa; const BIGNUM *bn; GetDSA(self, dsa); DSA_get0_key(dsa, &bn, NULL); return bn ? Qtrue : Qfalse; } /* * call-seq: * dsa.private? -> true | false * * Indicates whether this DSA instance has a private key associated with it or * not. The private key may be retrieved with DSA#private_key. */ static VALUE ossl_dsa_is_private(VALUE self) { DSA *dsa; GetDSA(self, dsa); return DSA_PRIVATE(self, dsa) ? Qtrue : Qfalse; } /* * call-seq: * dsa.export([cipher, password]) -> aString * dsa.to_pem([cipher, password]) -> aString * dsa.to_s([cipher, password]) -> aString * * Encodes this DSA to its PEM encoding. * * === Parameters * * +cipher+ is an OpenSSL::Cipher. * * +password+ is a string containing your password. * * === Examples * DSA.to_pem -> aString * DSA.to_pem(cipher, 'mypassword') -> aString * */ static VALUE ossl_dsa_export(int argc, VALUE *argv, VALUE self) { DSA *dsa; BIO *out; const EVP_CIPHER *ciph = NULL; VALUE cipher, pass, str; GetDSA(self, dsa); rb_scan_args(argc, argv, "02", &cipher, &pass); if (!NIL_P(cipher)) { ciph = GetCipherPtr(cipher); pass = ossl_pem_passwd_value(pass); } if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eDSAError, NULL); } if (DSA_HAS_PRIVATE(dsa)) { if (!PEM_write_bio_DSAPrivateKey(out, dsa, ciph, NULL, 0, ossl_pem_passwd_cb, (void *)pass)){ BIO_free(out); ossl_raise(eDSAError, NULL); } } else { if (!PEM_write_bio_DSA_PUBKEY(out, dsa)) { BIO_free(out); ossl_raise(eDSAError, NULL); } } str = ossl_membio2str(out); return str; } /* * call-seq: * dsa.to_der -> aString * * Encodes this DSA to its DER encoding. * */ static VALUE ossl_dsa_to_der(VALUE self) { DSA *dsa; int (*i2d_func)(DSA *, unsigned char **); unsigned char *p; long len; VALUE str; GetDSA(self, dsa); if(DSA_HAS_PRIVATE(dsa)) i2d_func = (int (*)(DSA *,unsigned char **))i2d_DSAPrivateKey; else i2d_func = i2d_DSA_PUBKEY; if((len = i2d_func(dsa, NULL)) <= 0) ossl_raise(eDSAError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_func(dsa, &p) < 0) ossl_raise(eDSAError, NULL); ossl_str_adjust(str, p); return str; } /* * call-seq: * dsa.params -> hash * * Stores all parameters of key to the hash * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! * Don't use :-)) (I's up to you) */ static VALUE ossl_dsa_get_params(VALUE self) { DSA *dsa; VALUE hash; const BIGNUM *p, *q, *g, *pub_key, *priv_key; GetDSA(self, dsa); DSA_get0_pqg(dsa, &p, &q, &g); DSA_get0_key(dsa, &pub_key, &priv_key); hash = rb_hash_new(); rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(p)); rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(q)); rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(g)); rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pub_key)); rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(priv_key)); return hash; } /* * call-seq: * dsa.to_text -> aString * * Prints all parameters of key to buffer * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!! * Don't use :-)) (I's up to you) */ static VALUE ossl_dsa_to_text(VALUE self) { DSA *dsa; BIO *out; VALUE str; GetDSA(self, dsa); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eDSAError, NULL); } if (!DSA_print(out, dsa, 0)) { /* offset = 0 */ BIO_free(out); ossl_raise(eDSAError, NULL); } str = ossl_membio2str(out); return str; } /* * call-seq: * dsa.public_key -> aDSA * * Returns a new DSA instance that carries just the public key information. * If the current instance has also private key information, this will no * longer be present in the new instance. This feature is helpful for * publishing the public key information without leaking any of the private * information. * * === Example * dsa = OpenSSL::PKey::DSA.new(2048) # has public and private information * pub_key = dsa.public_key # has only the public part available * pub_key_der = pub_key.to_der # it's safe to publish this * * */ static VALUE ossl_dsa_to_public_key(VALUE self) { EVP_PKEY *pkey; DSA *dsa; VALUE obj; GetPKeyDSA(self, pkey); /* err check performed by dsa_instance */ #define DSAPublicKey_dup(dsa) (DSA *)ASN1_dup( \ (i2d_of_void *)i2d_DSAPublicKey, (d2i_of_void *)d2i_DSAPublicKey, (char *)(dsa)) dsa = DSAPublicKey_dup(EVP_PKEY_get0_DSA(pkey)); #undef DSAPublicKey_dup obj = dsa_instance(rb_obj_class(self), dsa); if (obj == Qfalse) { DSA_free(dsa); ossl_raise(eDSAError, NULL); } return obj; } /* * call-seq: * dsa.syssign(string) -> aString * * Computes and returns the DSA signature of +string+, where +string+ is * expected to be an already-computed message digest of the original input * data. The signature is issued using the private key of this DSA instance. * * === Parameters * * +string+ is a message digest of the original input data to be signed * * === Example * dsa = OpenSSL::PKey::DSA.new(2048) * doc = "Sign me" * digest = OpenSSL::Digest::SHA1.digest(doc) * sig = dsa.syssign(digest) * * */ static VALUE ossl_dsa_sign(VALUE self, VALUE data) { DSA *dsa; const BIGNUM *dsa_q; unsigned int buf_len; VALUE str; GetDSA(self, dsa); DSA_get0_pqg(dsa, NULL, &dsa_q, NULL); if (!dsa_q) ossl_raise(eDSAError, "incomplete DSA"); if (!DSA_PRIVATE(self, dsa)) ossl_raise(eDSAError, "Private DSA key needed!"); StringValue(data); str = rb_str_new(0, DSA_size(dsa)); if (!DSA_sign(0, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *)RSTRING_PTR(str), &buf_len, dsa)) { /* type is ignored (0) */ ossl_raise(eDSAError, NULL); } rb_str_set_len(str, buf_len); return str; } /* * call-seq: * dsa.sysverify(digest, sig) -> true | false * * Verifies whether the signature is valid given the message digest input. It * does so by validating +sig+ using the public key of this DSA instance. * * === Parameters * * +digest+ is a message digest of the original input data to be signed * * +sig+ is a DSA signature value * * === Example * dsa = OpenSSL::PKey::DSA.new(2048) * doc = "Sign me" * digest = OpenSSL::Digest::SHA1.digest(doc) * sig = dsa.syssign(digest) * puts dsa.sysverify(digest, sig) # => true * */ static VALUE ossl_dsa_verify(VALUE self, VALUE digest, VALUE sig) { DSA *dsa; int ret; GetDSA(self, dsa); StringValue(digest); StringValue(sig); /* type is ignored (0) */ ret = DSA_verify(0, (unsigned char *)RSTRING_PTR(digest), RSTRING_LENINT(digest), (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), dsa); if (ret < 0) { ossl_raise(eDSAError, NULL); } else if (ret == 1) { return Qtrue; } return Qfalse; } /* * Document-method: OpenSSL::PKey::DSA#set_pqg * call-seq: * dsa.set_pqg(p, q, g) -> self * * Sets +p+, +q+, +g+ for the DSA instance. */ OSSL_PKEY_BN_DEF3(dsa, DSA, pqg, p, q, g) /* * Document-method: OpenSSL::PKey::DSA#set_key * call-seq: * dsa.set_key(pub_key, priv_key) -> self * * Sets +pub_key+ and +priv_key+ for the DSA instance. +priv_key+ may be nil. */ OSSL_PKEY_BN_DEF2(dsa, DSA, key, pub_key, priv_key) /* * INIT */ void Init_ossl_dsa(void) { #if 0 mPKey = rb_define_module_under(mOSSL, "PKey"); cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); #endif /* Document-class: OpenSSL::PKey::DSAError * * Generic exception that is raised if an operation on a DSA PKey * fails unexpectedly or in case an instantiation of an instance of DSA * fails due to non-conformant input data. */ eDSAError = rb_define_class_under(mPKey, "DSAError", ePKeyError); /* Document-class: OpenSSL::PKey::DSA * * DSA, the Digital Signature Algorithm, is specified in NIST's * FIPS 186-3. It is an asymmetric public key algorithm that may be used * similar to e.g. RSA. * Please note that for OpenSSL versions prior to 1.0.0 the digest * algorithms OpenSSL::Digest::DSS (equivalent to SHA) or * OpenSSL::Digest::DSS1 (equivalent to SHA-1) must be used for issuing * signatures with a DSA key using OpenSSL::PKey#sign. * Starting with OpenSSL 1.0.0, digest algorithms are no longer restricted, * any Digest may be used for signing. */ cDSA = rb_define_class_under(mPKey, "DSA", cPKey); rb_define_singleton_method(cDSA, "generate", ossl_dsa_s_generate, 1); rb_define_method(cDSA, "initialize", ossl_dsa_initialize, -1); rb_define_copy_func(cDSA, ossl_dsa_initialize_copy); rb_define_method(cDSA, "public?", ossl_dsa_is_public, 0); rb_define_method(cDSA, "private?", ossl_dsa_is_private, 0); rb_define_method(cDSA, "to_text", ossl_dsa_to_text, 0); rb_define_method(cDSA, "export", ossl_dsa_export, -1); rb_define_alias(cDSA, "to_pem", "export"); rb_define_alias(cDSA, "to_s", "export"); rb_define_method(cDSA, "to_der", ossl_dsa_to_der, 0); rb_define_method(cDSA, "public_key", ossl_dsa_to_public_key, 0); rb_define_method(cDSA, "syssign", ossl_dsa_sign, 1); rb_define_method(cDSA, "sysverify", ossl_dsa_verify, 2); DEF_OSSL_PKEY_BN(cDSA, dsa, p); DEF_OSSL_PKEY_BN(cDSA, dsa, q); DEF_OSSL_PKEY_BN(cDSA, dsa, g); DEF_OSSL_PKEY_BN(cDSA, dsa, pub_key); DEF_OSSL_PKEY_BN(cDSA, dsa, priv_key); rb_define_method(cDSA, "set_pqg", ossl_dsa_set_pqg, 3); rb_define_method(cDSA, "set_key", ossl_dsa_set_key, 2); rb_define_method(cDSA, "params", ossl_dsa_get_params, 0); } #else /* defined NO_DSA */ void Init_ossl_dsa(void) { } #endif /* NO_DSA */ openssl-2.0.9/ext/openssl/ossl_pkey_ec.c000066400000000000000000001321551336157045000203260ustar00rootroot00000000000000/* * Copyright (C) 2006-2007 Technorama Ltd. */ #include "ossl.h" #if !defined(OPENSSL_NO_EC) && (OPENSSL_VERSION_NUMBER >= 0x0090802fL) #define EXPORT_PEM 0 #define EXPORT_DER 1 static const rb_data_type_t ossl_ec_group_type; static const rb_data_type_t ossl_ec_point_type; #define GetPKeyEC(obj, pkey) do { \ GetPKey((obj), (pkey)); \ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { \ ossl_raise(rb_eRuntimeError, "THIS IS NOT A EC PKEY!"); \ } \ } while (0) #define GetEC(obj, key) do { \ EVP_PKEY *_pkey; \ GetPKeyEC(obj, _pkey); \ (key) = EVP_PKEY_get0_EC_KEY(_pkey); \ } while (0) #define SafeGetEC(obj, key) do { \ OSSL_Check_Kind(obj, cEC); \ GetEC(obj, key); \ } while (0) #define GetECGroup(obj, group) do { \ TypedData_Get_Struct(obj, EC_GROUP, &ossl_ec_group_type, group); \ if ((group) == NULL) \ ossl_raise(eEC_GROUP, "EC_GROUP is not initialized"); \ } while (0) #define SafeGetECGroup(obj, group) do { \ OSSL_Check_Kind((obj), cEC_GROUP); \ GetECGroup(obj, group); \ } while (0) #define GetECPoint(obj, point) do { \ TypedData_Get_Struct(obj, EC_POINT, &ossl_ec_point_type, point); \ if ((point) == NULL) \ ossl_raise(eEC_POINT, "EC_POINT is not initialized"); \ } while (0) #define SafeGetECPoint(obj, point) do { \ OSSL_Check_Kind((obj), cEC_POINT); \ GetECPoint(obj, point); \ } while(0) #define GetECPointGroup(obj, group) do { \ VALUE _group = rb_attr_get(obj, id_i_group); \ SafeGetECGroup(_group, group); \ } while (0) VALUE cEC; VALUE eECError; VALUE cEC_GROUP; VALUE eEC_GROUP; VALUE cEC_POINT; VALUE eEC_POINT; static ID s_GFp; static ID s_GFp_simple; static ID s_GFp_mont; static ID s_GFp_nist; static ID s_GF2m; static ID s_GF2m_simple; static ID ID_uncompressed; static ID ID_compressed; static ID ID_hybrid; static ID id_i_group; static VALUE ec_group_new(const EC_GROUP *group); static VALUE ec_point_new(const EC_POINT *point, const EC_GROUP *group); static VALUE ec_instance(VALUE klass, EC_KEY *ec) { EVP_PKEY *pkey; VALUE obj; if (!ec) { return Qfalse; } obj = NewPKey(klass); if (!(pkey = EVP_PKEY_new())) { return Qfalse; } if (!EVP_PKEY_assign_EC_KEY(pkey, ec)) { EVP_PKEY_free(pkey); return Qfalse; } SetPKey(obj, pkey); return obj; } VALUE ossl_ec_new(EVP_PKEY *pkey) { VALUE obj; if (!pkey) { obj = ec_instance(cEC, EC_KEY_new()); } else { obj = NewPKey(cEC); if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { ossl_raise(rb_eTypeError, "Not a EC key!"); } SetPKey(obj, pkey); } if (obj == Qfalse) { ossl_raise(eECError, NULL); } return obj; } /* * Creates a new EC_KEY on the EC group obj. arg can be an EC::Group or a String * representing an OID. */ static EC_KEY * ec_key_new_from_group(VALUE arg) { EC_KEY *ec; if (rb_obj_is_kind_of(arg, cEC_GROUP)) { EC_GROUP *group; SafeGetECGroup(arg, group); if (!(ec = EC_KEY_new())) ossl_raise(eECError, NULL); if (!EC_KEY_set_group(ec, group)) { EC_KEY_free(ec); ossl_raise(eECError, NULL); } } else { int nid = OBJ_sn2nid(StringValueCStr(arg)); if (nid == NID_undef) ossl_raise(eECError, "invalid curve name"); if (!(ec = EC_KEY_new_by_curve_name(nid))) ossl_raise(eECError, NULL); EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE); EC_KEY_set_conv_form(ec, POINT_CONVERSION_UNCOMPRESSED); } return ec; } /* * call-seq: * EC.generate(ec_group) -> ec * EC.generate(string) -> ec * * Creates a new EC instance with a new random private and public key. */ static VALUE ossl_ec_key_s_generate(VALUE klass, VALUE arg) { EC_KEY *ec; VALUE obj; ec = ec_key_new_from_group(arg); obj = ec_instance(klass, ec); if (obj == Qfalse) { EC_KEY_free(ec); ossl_raise(eECError, NULL); } if (!EC_KEY_generate_key(ec)) ossl_raise(eECError, "EC_KEY_generate_key"); return obj; } /* * call-seq: * OpenSSL::PKey::EC.new * OpenSSL::PKey::EC.new(ec_key) * OpenSSL::PKey::EC.new(ec_group) * OpenSSL::PKey::EC.new("secp112r1") * OpenSSL::PKey::EC.new(pem_string [, pwd]) * OpenSSL::PKey::EC.new(der_string) * * Creates a new EC object from given arguments. */ static VALUE ossl_ec_key_initialize(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; EC_KEY *ec; VALUE arg, pass; GetPKey(self, pkey); if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE) ossl_raise(eECError, "EC_KEY already initialized"); rb_scan_args(argc, argv, "02", &arg, &pass); if (NIL_P(arg)) { if (!(ec = EC_KEY_new())) ossl_raise(eECError, NULL); } else if (rb_obj_is_kind_of(arg, cEC)) { EC_KEY *other_ec = NULL; SafeGetEC(arg, other_ec); if (!(ec = EC_KEY_dup(other_ec))) ossl_raise(eECError, NULL); } else if (rb_obj_is_kind_of(arg, cEC_GROUP)) { ec = ec_key_new_from_group(arg); } else { BIO *in; pass = ossl_pem_passwd_value(pass); in = ossl_obj2bio(&arg); ec = PEM_read_bio_ECPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass); if (!ec) { OSSL_BIO_reset(in); ec = PEM_read_bio_EC_PUBKEY(in, NULL, ossl_pem_passwd_cb, (void *)pass); } if (!ec) { OSSL_BIO_reset(in); ec = d2i_ECPrivateKey_bio(in, NULL); } if (!ec) { OSSL_BIO_reset(in); ec = d2i_EC_PUBKEY_bio(in, NULL); } BIO_free(in); if (!ec) { ossl_clear_error(); ec = ec_key_new_from_group(arg); } } if (!EVP_PKEY_assign_EC_KEY(pkey, ec)) { EC_KEY_free(ec); ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY"); } return self; } static VALUE ossl_ec_key_initialize_copy(VALUE self, VALUE other) { EVP_PKEY *pkey; EC_KEY *ec, *ec_new; GetPKey(self, pkey); if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE) ossl_raise(eECError, "EC already initialized"); SafeGetEC(other, ec); ec_new = EC_KEY_dup(ec); if (!ec_new) ossl_raise(eECError, "EC_KEY_dup"); if (!EVP_PKEY_assign_EC_KEY(pkey, ec_new)) { EC_KEY_free(ec_new); ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY"); } return self; } /* * call-seq: * key.group => group * * Returns the EC::Group that the key is associated with. Modifying the returned * group does not affect +key+. */ static VALUE ossl_ec_key_get_group(VALUE self) { EC_KEY *ec; const EC_GROUP *group; GetEC(self, ec); group = EC_KEY_get0_group(ec); if (!group) return Qnil; return ec_group_new(group); } /* * call-seq: * key.group = group * * Sets the EC::Group for the key. The group structure is internally copied so * modification to +group+ after assigning to a key has no effect on the key. */ static VALUE ossl_ec_key_set_group(VALUE self, VALUE group_v) { EC_KEY *ec; EC_GROUP *group; GetEC(self, ec); SafeGetECGroup(group_v, group); if (EC_KEY_set_group(ec, group) != 1) ossl_raise(eECError, "EC_KEY_set_group"); return group_v; } /* * call-seq: * key.private_key => OpenSSL::BN * * See the OpenSSL documentation for EC_KEY_get0_private_key() */ static VALUE ossl_ec_key_get_private_key(VALUE self) { EC_KEY *ec; const BIGNUM *bn; GetEC(self, ec); if ((bn = EC_KEY_get0_private_key(ec)) == NULL) return Qnil; return ossl_bn_new(bn); } /* * call-seq: * key.private_key = openssl_bn * * See the OpenSSL documentation for EC_KEY_set_private_key() */ static VALUE ossl_ec_key_set_private_key(VALUE self, VALUE private_key) { EC_KEY *ec; BIGNUM *bn = NULL; GetEC(self, ec); if (!NIL_P(private_key)) bn = GetBNPtr(private_key); switch (EC_KEY_set_private_key(ec, bn)) { case 1: break; case 0: if (bn == NULL) break; default: ossl_raise(eECError, "EC_KEY_set_private_key"); } return private_key; } /* * call-seq: * key.public_key => OpenSSL::PKey::EC::Point * * See the OpenSSL documentation for EC_KEY_get0_public_key() */ static VALUE ossl_ec_key_get_public_key(VALUE self) { EC_KEY *ec; const EC_POINT *point; GetEC(self, ec); if ((point = EC_KEY_get0_public_key(ec)) == NULL) return Qnil; return ec_point_new(point, EC_KEY_get0_group(ec)); } /* * call-seq: * key.public_key = ec_point * * See the OpenSSL documentation for EC_KEY_set_public_key() */ static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key) { EC_KEY *ec; EC_POINT *point = NULL; GetEC(self, ec); if (!NIL_P(public_key)) SafeGetECPoint(public_key, point); switch (EC_KEY_set_public_key(ec, point)) { case 1: break; case 0: if (point == NULL) break; default: ossl_raise(eECError, "EC_KEY_set_public_key"); } return public_key; } /* * call-seq: * key.public? => true or false * * Returns whether this EC instance has a public key. The public key * (EC::Point) can be retrieved with EC#public_key. */ static VALUE ossl_ec_key_is_public(VALUE self) { EC_KEY *ec; GetEC(self, ec); return EC_KEY_get0_public_key(ec) ? Qtrue : Qfalse; } /* * call-seq: * key.private? => true or false * * Returns whether this EC instance has a private key. The private key (BN) can * be retrieved with EC#private_key. */ static VALUE ossl_ec_key_is_private(VALUE self) { EC_KEY *ec; GetEC(self, ec); return EC_KEY_get0_private_key(ec) ? Qtrue : Qfalse; } static VALUE ossl_ec_key_to_string(VALUE self, VALUE ciph, VALUE pass, int format) { EC_KEY *ec; BIO *out; int i = -1; int private = 0; VALUE str; const EVP_CIPHER *cipher = NULL; GetEC(self, ec); if (EC_KEY_get0_public_key(ec) == NULL) ossl_raise(eECError, "can't export - no public key set"); if (EC_KEY_check_key(ec) != 1) ossl_raise(eECError, "can't export - EC_KEY_check_key failed"); if (EC_KEY_get0_private_key(ec)) private = 1; if (!NIL_P(ciph)) { cipher = GetCipherPtr(ciph); pass = ossl_pem_passwd_value(pass); } if (!(out = BIO_new(BIO_s_mem()))) ossl_raise(eECError, "BIO_new(BIO_s_mem())"); switch(format) { case EXPORT_PEM: if (private) { i = PEM_write_bio_ECPrivateKey(out, ec, cipher, NULL, 0, ossl_pem_passwd_cb, (void *)pass); } else { i = PEM_write_bio_EC_PUBKEY(out, ec); } break; case EXPORT_DER: if (private) { i = i2d_ECPrivateKey_bio(out, ec); } else { i = i2d_EC_PUBKEY_bio(out, ec); } break; default: BIO_free(out); ossl_raise(rb_eRuntimeError, "unknown format (internal error)"); } if (i != 1) { BIO_free(out); ossl_raise(eECError, "outlen=%d", i); } str = ossl_membio2str(out); return str; } /* * call-seq: * key.export([cipher, pass_phrase]) => String * key.to_pem([cipher, pass_phrase]) => String * * Outputs the EC key in PEM encoding. If +cipher+ and +pass_phrase+ are given * they will be used to encrypt the key. +cipher+ must be an OpenSSL::Cipher * instance. Note that encryption will only be effective for a private key, * public keys will always be encoded in plain text. */ static VALUE ossl_ec_key_export(int argc, VALUE *argv, VALUE self) { VALUE cipher, passwd; rb_scan_args(argc, argv, "02", &cipher, &passwd); return ossl_ec_key_to_string(self, cipher, passwd, EXPORT_PEM); } /* * call-seq: * key.to_der => String * * See the OpenSSL documentation for i2d_ECPrivateKey_bio() */ static VALUE ossl_ec_key_to_der(VALUE self) { return ossl_ec_key_to_string(self, Qnil, Qnil, EXPORT_DER); } /* * call-seq: * key.to_text => String * * See the OpenSSL documentation for EC_KEY_print() */ static VALUE ossl_ec_key_to_text(VALUE self) { EC_KEY *ec; BIO *out; VALUE str; GetEC(self, ec); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eECError, "BIO_new(BIO_s_mem())"); } if (!EC_KEY_print(out, ec, 0)) { BIO_free(out); ossl_raise(eECError, "EC_KEY_print"); } str = ossl_membio2str(out); return str; } /* * call-seq: * key.generate_key! => self * * Generates a new random private and public key. * * See also the OpenSSL documentation for EC_KEY_generate_key() * * === Example * ec = OpenSSL::PKey::EC.new("prime256v1") * p ec.private_key # => nil * ec.generate_key! * p ec.private_key # => # */ static VALUE ossl_ec_key_generate_key(VALUE self) { EC_KEY *ec; GetEC(self, ec); if (EC_KEY_generate_key(ec) != 1) ossl_raise(eECError, "EC_KEY_generate_key"); return self; } /* * call-seq: * key.check_key => true * * Raises an exception if the key is invalid. * * See the OpenSSL documentation for EC_KEY_check_key() */ static VALUE ossl_ec_key_check_key(VALUE self) { EC_KEY *ec; GetEC(self, ec); if (EC_KEY_check_key(ec) != 1) ossl_raise(eECError, "EC_KEY_check_key"); return Qtrue; } /* * call-seq: * key.dh_compute_key(pubkey) => String * * See the OpenSSL documentation for ECDH_compute_key() */ static VALUE ossl_ec_key_dh_compute_key(VALUE self, VALUE pubkey) { EC_KEY *ec; EC_POINT *point; int buf_len; VALUE str; GetEC(self, ec); SafeGetECPoint(pubkey, point); /* BUG: need a way to figure out the maximum string size */ buf_len = 1024; str = rb_str_new(0, buf_len); /* BUG: take KDF as a block */ buf_len = ECDH_compute_key(RSTRING_PTR(str), buf_len, point, ec, NULL); if (buf_len < 0) ossl_raise(eECError, "ECDH_compute_key"); rb_str_resize(str, buf_len); return str; } /* sign_setup */ /* * call-seq: * key.dsa_sign_asn1(data) => String * * See the OpenSSL documentation for ECDSA_sign() */ static VALUE ossl_ec_key_dsa_sign_asn1(VALUE self, VALUE data) { EC_KEY *ec; unsigned int buf_len; VALUE str; GetEC(self, ec); StringValue(data); if (EC_KEY_get0_private_key(ec) == NULL) ossl_raise(eECError, "Private EC key needed!"); str = rb_str_new(0, ECDSA_size(ec)); if (ECDSA_sign(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *) RSTRING_PTR(str), &buf_len, ec) != 1) ossl_raise(eECError, "ECDSA_sign"); rb_str_set_len(str, buf_len); return str; } /* * call-seq: * key.dsa_verify_asn1(data, sig) => true or false * * See the OpenSSL documentation for ECDSA_verify() */ static VALUE ossl_ec_key_dsa_verify_asn1(VALUE self, VALUE data, VALUE sig) { EC_KEY *ec; GetEC(self, ec); StringValue(data); StringValue(sig); switch (ECDSA_verify(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *) RSTRING_PTR(sig), (int)RSTRING_LEN(sig), ec)) { case 1: return Qtrue; case 0: return Qfalse; default: break; } ossl_raise(eECError, "ECDSA_verify"); UNREACHABLE; } /* * OpenSSL::PKey::EC::Group */ static void ossl_ec_group_free(void *ptr) { EC_GROUP_clear_free(ptr); } static const rb_data_type_t ossl_ec_group_type = { "OpenSSL/ec_group", { 0, ossl_ec_group_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static VALUE ossl_ec_group_alloc(VALUE klass) { return TypedData_Wrap_Struct(klass, &ossl_ec_group_type, NULL); } static VALUE ec_group_new(const EC_GROUP *group) { VALUE obj; EC_GROUP *group_new; obj = ossl_ec_group_alloc(cEC_GROUP); group_new = EC_GROUP_dup(group); if (!group_new) ossl_raise(eEC_GROUP, "EC_GROUP_dup"); RTYPEDDATA_DATA(obj) = group_new; return obj; } /* * call-seq: * OpenSSL::PKey::EC::Group.new(ec_group) * OpenSSL::PKey::EC::Group.new(pem_or_der_encoded) * OpenSSL::PKey::EC::Group.new(ec_method) * OpenSSL::PKey::EC::Group.new(:GFp, bignum_p, bignum_a, bignum_b) * OpenSSL::PKey::EC::Group.new(:GF2m, bignum_p, bignum_a, bignum_b) * * Creates a new EC::Group object. * * +ec_method+ is a symbol that represents an EC_METHOD. Currently the following * are supported: * * * :GFp_simple * * :GFp_mont * * :GFp_nist * * :GF2m_simple * * If the first argument is :GFp or :GF2m, creates a new curve with given * parameters. */ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) { VALUE arg1, arg2, arg3, arg4; EC_GROUP *group; TypedData_Get_Struct(self, EC_GROUP, &ossl_ec_group_type, group); if (group) ossl_raise(rb_eRuntimeError, "EC_GROUP is already initialized"); switch (rb_scan_args(argc, argv, "13", &arg1, &arg2, &arg3, &arg4)) { case 1: if (SYMBOL_P(arg1)) { const EC_METHOD *method = NULL; ID id = SYM2ID(arg1); if (id == s_GFp_simple) { method = EC_GFp_simple_method(); } else if (id == s_GFp_mont) { method = EC_GFp_mont_method(); } else if (id == s_GFp_nist) { method = EC_GFp_nist_method(); #if !defined(OPENSSL_NO_EC2M) } else if (id == s_GF2m_simple) { method = EC_GF2m_simple_method(); #endif } if (method) { if ((group = EC_GROUP_new(method)) == NULL) ossl_raise(eEC_GROUP, "EC_GROUP_new"); } else { ossl_raise(rb_eArgError, "unknown symbol, must be :GFp_simple, :GFp_mont, :GFp_nist or :GF2m_simple"); } } else if (rb_obj_is_kind_of(arg1, cEC_GROUP)) { const EC_GROUP *arg1_group; SafeGetECGroup(arg1, arg1_group); if ((group = EC_GROUP_dup(arg1_group)) == NULL) ossl_raise(eEC_GROUP, "EC_GROUP_dup"); } else { BIO *in = ossl_obj2bio(&arg1); group = PEM_read_bio_ECPKParameters(in, NULL, NULL, NULL); if (!group) { OSSL_BIO_reset(in); group = d2i_ECPKParameters_bio(in, NULL); } BIO_free(in); if (!group) { const char *name = StringValueCStr(arg1); int nid = OBJ_sn2nid(name); ossl_clear_error(); /* ignore errors in d2i_ECPKParameters_bio() */ if (nid == NID_undef) ossl_raise(eEC_GROUP, "unknown curve name (%"PRIsVALUE")", arg1); group = EC_GROUP_new_by_curve_name(nid); if (group == NULL) ossl_raise(eEC_GROUP, "unable to create curve (%"PRIsVALUE")", arg1); EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED); } } break; case 4: if (SYMBOL_P(arg1)) { ID id = SYM2ID(arg1); EC_GROUP *(*new_curve)(const BIGNUM *, const BIGNUM *, const BIGNUM *, BN_CTX *) = NULL; const BIGNUM *p = GetBNPtr(arg2); const BIGNUM *a = GetBNPtr(arg3); const BIGNUM *b = GetBNPtr(arg4); if (id == s_GFp) { new_curve = EC_GROUP_new_curve_GFp; #if !defined(OPENSSL_NO_EC2M) } else if (id == s_GF2m) { new_curve = EC_GROUP_new_curve_GF2m; #endif } else { ossl_raise(rb_eArgError, "unknown symbol, must be :GFp or :GF2m"); } if ((group = new_curve(p, a, b, ossl_bn_ctx)) == NULL) ossl_raise(eEC_GROUP, "EC_GROUP_new_by_GF*"); } else { ossl_raise(rb_eArgError, "unknown argument, must be :GFp or :GF2m"); } break; default: ossl_raise(rb_eArgError, "wrong number of arguments"); } if (group == NULL) ossl_raise(eEC_GROUP, ""); RTYPEDDATA_DATA(self) = group; return self; } static VALUE ossl_ec_group_initialize_copy(VALUE self, VALUE other) { EC_GROUP *group, *group_new; TypedData_Get_Struct(self, EC_GROUP, &ossl_ec_group_type, group_new); if (group_new) ossl_raise(eEC_GROUP, "EC::Group already initialized"); SafeGetECGroup(other, group); group_new = EC_GROUP_dup(group); if (!group_new) ossl_raise(eEC_GROUP, "EC_GROUP_dup"); RTYPEDDATA_DATA(self) = group_new; return self; } /* * call-seq: * group1.eql?(group2) => true | false * group1 == group2 => true | false * * Returns true if the two groups use the same curve and have the same * parameters, false otherwise. */ static VALUE ossl_ec_group_eql(VALUE a, VALUE b) { EC_GROUP *group1 = NULL, *group2 = NULL; GetECGroup(a, group1); SafeGetECGroup(b, group2); if (EC_GROUP_cmp(group1, group2, ossl_bn_ctx) == 1) return Qfalse; return Qtrue; } /* * call-seq: * group.generator => ec_point * * Returns the generator of the group. * * See the OpenSSL documentation for EC_GROUP_get0_generator() */ static VALUE ossl_ec_group_get_generator(VALUE self) { EC_GROUP *group; const EC_POINT *generator; GetECGroup(self, group); generator = EC_GROUP_get0_generator(group); if (!generator) return Qnil; return ec_point_new(generator, group); } /* * call-seq: * group.set_generator(generator, order, cofactor) => self * * Sets the curve parameters. +generator+ must be an instance of EC::Point that * is on the curve. +order+ and +cofactor+ are integers. * * See the OpenSSL documentation for EC_GROUP_set_generator() */ static VALUE ossl_ec_group_set_generator(VALUE self, VALUE generator, VALUE order, VALUE cofactor) { EC_GROUP *group = NULL; const EC_POINT *point; const BIGNUM *o, *co; GetECGroup(self, group); SafeGetECPoint(generator, point); o = GetBNPtr(order); co = GetBNPtr(cofactor); if (EC_GROUP_set_generator(group, point, o, co) != 1) ossl_raise(eEC_GROUP, "EC_GROUP_set_generator"); return self; } /* * call-seq: * group.get_order => order_bn * * Returns the order of the group. * * See the OpenSSL documentation for EC_GROUP_get_order() */ static VALUE ossl_ec_group_get_order(VALUE self) { VALUE bn_obj; BIGNUM *bn; EC_GROUP *group = NULL; GetECGroup(self, group); bn_obj = ossl_bn_new(NULL); bn = GetBNPtr(bn_obj); if (EC_GROUP_get_order(group, bn, ossl_bn_ctx) != 1) ossl_raise(eEC_GROUP, "EC_GROUP_get_order"); return bn_obj; } /* * call-seq: * group.get_cofactor => cofactor_bn * * Returns the cofactor of the group. * * See the OpenSSL documentation for EC_GROUP_get_cofactor() */ static VALUE ossl_ec_group_get_cofactor(VALUE self) { VALUE bn_obj; BIGNUM *bn; EC_GROUP *group = NULL; GetECGroup(self, group); bn_obj = ossl_bn_new(NULL); bn = GetBNPtr(bn_obj); if (EC_GROUP_get_cofactor(group, bn, ossl_bn_ctx) != 1) ossl_raise(eEC_GROUP, "EC_GROUP_get_cofactor"); return bn_obj; } /* * call-seq: * group.curve_name => String * * Returns the curve name (sn). * * See the OpenSSL documentation for EC_GROUP_get_curve_name() */ static VALUE ossl_ec_group_get_curve_name(VALUE self) { EC_GROUP *group = NULL; int nid; GetECGroup(self, group); if (group == NULL) return Qnil; nid = EC_GROUP_get_curve_name(group); /* BUG: an nid or asn1 object should be returned, maybe. */ return rb_str_new2(OBJ_nid2sn(nid)); } /* * call-seq: * EC.builtin_curves => [[sn, comment], ...] * * Obtains a list of all predefined curves by the OpenSSL. Curve names are * returned as sn. * * See the OpenSSL documentation for EC_get_builtin_curves(). */ static VALUE ossl_s_builtin_curves(VALUE self) { EC_builtin_curve *curves = NULL; int n; int crv_len = rb_long2int(EC_get_builtin_curves(NULL, 0)); VALUE ary, ret; curves = ALLOCA_N(EC_builtin_curve, crv_len); if (curves == NULL) return Qnil; if (!EC_get_builtin_curves(curves, crv_len)) ossl_raise(rb_eRuntimeError, "EC_get_builtin_curves"); ret = rb_ary_new2(crv_len); for (n = 0; n < crv_len; n++) { const char *sname = OBJ_nid2sn(curves[n].nid); const char *comment = curves[n].comment; ary = rb_ary_new2(2); rb_ary_push(ary, rb_str_new2(sname)); rb_ary_push(ary, comment ? rb_str_new2(comment) : Qnil); rb_ary_push(ret, ary); } return ret; } /* * call-seq: * group.asn1_flag -> Integer * * Returns the flags set on the group. * * See also #asn1_flag=. */ static VALUE ossl_ec_group_get_asn1_flag(VALUE self) { EC_GROUP *group = NULL; int flag; GetECGroup(self, group); flag = EC_GROUP_get_asn1_flag(group); return INT2NUM(flag); } /* * call-seq: * group.asn1_flag = flags * * Sets flags on the group. The flag value is used to determine how to encode * the group: encode explicit parameters or named curve using an OID. * * The flag value can be either of: * * * EC::NAMED_CURVE * * EC::EXPLICIT_CURVE * * See the OpenSSL documentation for EC_GROUP_set_asn1_flag(). */ static VALUE ossl_ec_group_set_asn1_flag(VALUE self, VALUE flag_v) { EC_GROUP *group = NULL; GetECGroup(self, group); EC_GROUP_set_asn1_flag(group, NUM2INT(flag_v)); return flag_v; } /* * call-seq: * group.point_conversion_form -> Symbol * * Returns the form how EC::Point data is encoded as ASN.1. * * See also #point_conversion_form=. */ static VALUE ossl_ec_group_get_point_conversion_form(VALUE self) { EC_GROUP *group = NULL; point_conversion_form_t form; VALUE ret; GetECGroup(self, group); form = EC_GROUP_get_point_conversion_form(group); switch (form) { case POINT_CONVERSION_UNCOMPRESSED: ret = ID_uncompressed; break; case POINT_CONVERSION_COMPRESSED: ret = ID_compressed; break; case POINT_CONVERSION_HYBRID: ret = ID_hybrid; break; default: ossl_raise(eEC_GROUP, "unsupported point conversion form: %d, this module should be updated", form); } return ID2SYM(ret); } static point_conversion_form_t parse_point_conversion_form_symbol(VALUE sym) { ID id = SYM2ID(sym); if (id == ID_uncompressed) return POINT_CONVERSION_UNCOMPRESSED; else if (id == ID_compressed) return POINT_CONVERSION_COMPRESSED; else if (id == ID_hybrid) return POINT_CONVERSION_HYBRID; else ossl_raise(rb_eArgError, "unsupported point conversion form %+"PRIsVALUE " (expected :compressed, :uncompressed, or :hybrid)", sym); } /* * call-seq: * group.point_conversion_form = form * * Sets the form how EC::Point data is encoded as ASN.1 as defined in X9.62. * * +format+ can be one of these: * * :compressed:: * Encoded as z||x, where z is an octet indicating which solution of the * equation y is. z will be 0x02 or 0x03. * :uncompressed:: * Encoded as z||x||y, where z is an octet 0x04. * :hybrid:: * Encodes as z||x||y, where z is an octet indicating which solution of the * equation y is. z will be 0x06 or 0x07. * * See the OpenSSL documentation for EC_GROUP_set_point_conversion_form() */ static VALUE ossl_ec_group_set_point_conversion_form(VALUE self, VALUE form_v) { EC_GROUP *group; point_conversion_form_t form; GetECGroup(self, group); form = parse_point_conversion_form_symbol(form_v); EC_GROUP_set_point_conversion_form(group, form); return form_v; } /* * call-seq: * group.seed => String or nil * * See the OpenSSL documentation for EC_GROUP_get0_seed() */ static VALUE ossl_ec_group_get_seed(VALUE self) { EC_GROUP *group = NULL; size_t seed_len; GetECGroup(self, group); seed_len = EC_GROUP_get_seed_len(group); if (seed_len == 0) return Qnil; return rb_str_new((const char *)EC_GROUP_get0_seed(group), seed_len); } /* * call-seq: * group.seed = seed => seed * * See the OpenSSL documentation for EC_GROUP_set_seed() */ static VALUE ossl_ec_group_set_seed(VALUE self, VALUE seed) { EC_GROUP *group = NULL; GetECGroup(self, group); StringValue(seed); if (EC_GROUP_set_seed(group, (unsigned char *)RSTRING_PTR(seed), RSTRING_LEN(seed)) != (size_t)RSTRING_LEN(seed)) ossl_raise(eEC_GROUP, "EC_GROUP_set_seed"); return seed; } /* get/set curve GFp, GF2m */ /* * call-seq: * group.degree => integer * * See the OpenSSL documentation for EC_GROUP_get_degree() */ static VALUE ossl_ec_group_get_degree(VALUE self) { EC_GROUP *group = NULL; GetECGroup(self, group); return INT2NUM(EC_GROUP_get_degree(group)); } static VALUE ossl_ec_group_to_string(VALUE self, int format) { EC_GROUP *group; BIO *out; int i = -1; VALUE str; GetECGroup(self, group); if (!(out = BIO_new(BIO_s_mem()))) ossl_raise(eEC_GROUP, "BIO_new(BIO_s_mem())"); switch(format) { case EXPORT_PEM: i = PEM_write_bio_ECPKParameters(out, group); break; case EXPORT_DER: i = i2d_ECPKParameters_bio(out, group); break; default: BIO_free(out); ossl_raise(rb_eRuntimeError, "unknown format (internal error)"); } if (i != 1) { BIO_free(out); ossl_raise(eECError, NULL); } str = ossl_membio2str(out); return str; } /* * call-seq: * group.to_pem => String * * See the OpenSSL documentation for PEM_write_bio_ECPKParameters() */ static VALUE ossl_ec_group_to_pem(VALUE self) { return ossl_ec_group_to_string(self, EXPORT_PEM); } /* * call-seq: * group.to_der => String * * See the OpenSSL documentation for i2d_ECPKParameters_bio() */ static VALUE ossl_ec_group_to_der(VALUE self) { return ossl_ec_group_to_string(self, EXPORT_DER); } /* * call-seq: * group.to_text => String * * See the OpenSSL documentation for ECPKParameters_print() */ static VALUE ossl_ec_group_to_text(VALUE self) { EC_GROUP *group; BIO *out; VALUE str; GetECGroup(self, group); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eEC_GROUP, "BIO_new(BIO_s_mem())"); } if (!ECPKParameters_print(out, group, 0)) { BIO_free(out); ossl_raise(eEC_GROUP, NULL); } str = ossl_membio2str(out); return str; } /* * OpenSSL::PKey::EC::Point */ static void ossl_ec_point_free(void *ptr) { EC_POINT_clear_free(ptr); } static const rb_data_type_t ossl_ec_point_type = { "OpenSSL/EC_POINT", { 0, ossl_ec_point_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static VALUE ossl_ec_point_alloc(VALUE klass) { return TypedData_Wrap_Struct(klass, &ossl_ec_point_type, NULL); } static VALUE ec_point_new(const EC_POINT *point, const EC_GROUP *group) { EC_POINT *point_new; VALUE obj; obj = ossl_ec_point_alloc(cEC_POINT); point_new = EC_POINT_dup(point, group); if (!point_new) ossl_raise(eEC_POINT, "EC_POINT_dup"); RTYPEDDATA_DATA(obj) = point_new; rb_ivar_set(obj, id_i_group, ec_group_new(group)); return obj; } /* * call-seq: * OpenSSL::PKey::EC::Point.new(point) * OpenSSL::PKey::EC::Point.new(group) * OpenSSL::PKey::EC::Point.new(group, bn) * * See the OpenSSL documentation for EC_POINT_* */ static VALUE ossl_ec_point_initialize(int argc, VALUE *argv, VALUE self) { EC_POINT *point; VALUE arg1, arg2; VALUE group_v = Qnil; const EC_GROUP *group = NULL; TypedData_Get_Struct(self, EC_POINT, &ossl_ec_point_type, point); if (point) ossl_raise(eEC_POINT, "EC_POINT already initialized"); switch (rb_scan_args(argc, argv, "11", &arg1, &arg2)) { case 1: if (rb_obj_is_kind_of(arg1, cEC_POINT)) { const EC_POINT *arg_point; group_v = rb_attr_get(arg1, id_i_group); SafeGetECGroup(group_v, group); SafeGetECPoint(arg1, arg_point); point = EC_POINT_dup(arg_point, group); } else if (rb_obj_is_kind_of(arg1, cEC_GROUP)) { group_v = arg1; SafeGetECGroup(group_v, group); point = EC_POINT_new(group); } else { ossl_raise(eEC_POINT, "wrong argument type: must be OpenSSL::PKey::EC::Point or OpenSSL::Pkey::EC::Group"); } break; case 2: if (!rb_obj_is_kind_of(arg1, cEC_GROUP)) ossl_raise(rb_eArgError, "1st argument must be OpenSSL::PKey::EC::Group"); group_v = arg1; SafeGetECGroup(group_v, group); if (rb_obj_is_kind_of(arg2, cBN)) { const BIGNUM *bn = GetBNPtr(arg2); point = EC_POINT_bn2point(group, bn, NULL, ossl_bn_ctx); } else { BIO *in = ossl_obj2bio(&arg1); /* BUG: finish me */ BIO_free(in); if (point == NULL) { ossl_raise(eEC_POINT, "unknown type for 2nd arg"); } } break; default: ossl_raise(rb_eArgError, "wrong number of arguments"); } if (point == NULL) ossl_raise(eEC_POINT, NULL); if (NIL_P(group_v)) ossl_raise(rb_eRuntimeError, "missing group (internal error)"); RTYPEDDATA_DATA(self) = point; rb_ivar_set(self, id_i_group, group_v); return self; } static VALUE ossl_ec_point_initialize_copy(VALUE self, VALUE other) { EC_POINT *point, *point_new; EC_GROUP *group; VALUE group_v; TypedData_Get_Struct(self, EC_POINT, &ossl_ec_point_type, point_new); if (point_new) ossl_raise(eEC_POINT, "EC::Point already initialized"); SafeGetECPoint(other, point); group_v = rb_obj_dup(rb_attr_get(other, id_i_group)); SafeGetECGroup(group_v, group); point_new = EC_POINT_dup(point, group); if (!point_new) ossl_raise(eEC_POINT, "EC_POINT_dup"); RTYPEDDATA_DATA(self) = point_new; rb_ivar_set(self, id_i_group, group_v); return self; } /* * call-seq: * point1.eql?(point2) => true | false * point1 == point2 => true | false */ static VALUE ossl_ec_point_eql(VALUE a, VALUE b) { EC_POINT *point1, *point2; VALUE group_v1 = rb_attr_get(a, id_i_group); VALUE group_v2 = rb_attr_get(b, id_i_group); const EC_GROUP *group; if (ossl_ec_group_eql(group_v1, group_v2) == Qfalse) return Qfalse; GetECPoint(a, point1); SafeGetECPoint(b, point2); SafeGetECGroup(group_v1, group); if (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx) == 1) return Qfalse; return Qtrue; } /* * call-seq: * point.infinity? => true | false */ static VALUE ossl_ec_point_is_at_infinity(VALUE self) { EC_POINT *point; const EC_GROUP *group; GetECPoint(self, point); GetECPointGroup(self, group); switch (EC_POINT_is_at_infinity(group, point)) { case 1: return Qtrue; case 0: return Qfalse; default: ossl_raise(cEC_POINT, "EC_POINT_is_at_infinity"); } UNREACHABLE; } /* * call-seq: * point.on_curve? => true | false */ static VALUE ossl_ec_point_is_on_curve(VALUE self) { EC_POINT *point; const EC_GROUP *group; GetECPoint(self, point); GetECPointGroup(self, group); switch (EC_POINT_is_on_curve(group, point, ossl_bn_ctx)) { case 1: return Qtrue; case 0: return Qfalse; default: ossl_raise(cEC_POINT, "EC_POINT_is_on_curve"); } UNREACHABLE; } /* * call-seq: * point.make_affine! => self */ static VALUE ossl_ec_point_make_affine(VALUE self) { EC_POINT *point; const EC_GROUP *group; GetECPoint(self, point); GetECPointGroup(self, group); if (EC_POINT_make_affine(group, point, ossl_bn_ctx) != 1) ossl_raise(cEC_POINT, "EC_POINT_make_affine"); return self; } /* * call-seq: * point.invert! => self */ static VALUE ossl_ec_point_invert(VALUE self) { EC_POINT *point; const EC_GROUP *group; GetECPoint(self, point); GetECPointGroup(self, group); if (EC_POINT_invert(group, point, ossl_bn_ctx) != 1) ossl_raise(cEC_POINT, "EC_POINT_invert"); return self; } /* * call-seq: * point.set_to_infinity! => self */ static VALUE ossl_ec_point_set_to_infinity(VALUE self) { EC_POINT *point; const EC_GROUP *group; GetECPoint(self, point); GetECPointGroup(self, group); if (EC_POINT_set_to_infinity(group, point) != 1) ossl_raise(cEC_POINT, "EC_POINT_set_to_infinity"); return self; } /* * call-seq: * point.to_bn(conversion_form = nil) => OpenSSL::BN * * Convert the EC point into an octet string and store in an OpenSSL::BN. If * +conversion_form+ is given, the point data is converted using the specified * form. If not given, the default form set in the EC::Group object is used. * * See also EC::Point#point_conversion_form=. */ static VALUE ossl_ec_point_to_bn(int argc, VALUE *argv, VALUE self) { EC_POINT *point; VALUE form_obj, bn_obj; const EC_GROUP *group; point_conversion_form_t form; BIGNUM *bn; GetECPoint(self, point); GetECPointGroup(self, group); rb_scan_args(argc, argv, "01", &form_obj); if (NIL_P(form_obj)) form = EC_GROUP_get_point_conversion_form(group); else form = parse_point_conversion_form_symbol(form_obj); bn_obj = rb_obj_alloc(cBN); bn = GetBNPtr(bn_obj); if (EC_POINT_point2bn(group, point, form, bn, ossl_bn_ctx) == NULL) ossl_raise(eEC_POINT, "EC_POINT_point2bn"); return bn_obj; } /* * call-seq: * point.mul(bn1 [, bn2]) => point * point.mul(bns, points [, bn2]) => point * * Performs elliptic curve point multiplication. * * The first form calculates bn1 * point + bn2 * G, where +G+ is the * generator of the group of +point+. +bn2+ may be omitted, and in that case, * the result is just bn1 * point. * * The second form calculates bns[0] * point + bns[1] * points[0] + ... * + bns[-1] * points[-1] + bn2 * G. +bn2+ may be omitted. +bns+ must be * an array of OpenSSL::BN. +points+ must be an array of * OpenSSL::PKey::EC::Point. Please note that points[0] is not * multiplied by bns[0], but bns[1]. */ static VALUE ossl_ec_point_mul(int argc, VALUE *argv, VALUE self) { EC_POINT *point_self, *point_result; const EC_GROUP *group; VALUE group_v = rb_attr_get(self, id_i_group); VALUE arg1, arg2, arg3, result; const BIGNUM *bn_g = NULL; GetECPoint(self, point_self); SafeGetECGroup(group_v, group); result = rb_obj_alloc(cEC_POINT); ossl_ec_point_initialize(1, &group_v, result); GetECPoint(result, point_result); rb_scan_args(argc, argv, "12", &arg1, &arg2, &arg3); if (!RB_TYPE_P(arg1, T_ARRAY)) { BIGNUM *bn = GetBNPtr(arg1); if (!NIL_P(arg2)) bn_g = GetBNPtr(arg2); if (EC_POINT_mul(group, point_result, bn_g, point_self, bn, ossl_bn_ctx) != 1) ossl_raise(eEC_POINT, NULL); } else { /* * bignums | arg1[0] | arg1[1] | arg1[2] | ... * points | self | arg2[0] | arg2[1] | ... */ long i, num; VALUE bns_tmp, tmp_p, tmp_b; const EC_POINT **points; const BIGNUM **bignums; Check_Type(arg1, T_ARRAY); Check_Type(arg2, T_ARRAY); if (RARRAY_LEN(arg1) != RARRAY_LEN(arg2) + 1) /* arg2 must be 1 larger */ ossl_raise(rb_eArgError, "bns must be 1 longer than points; see the documentation"); num = RARRAY_LEN(arg1); bns_tmp = rb_ary_tmp_new(num); bignums = ALLOCV_N(const BIGNUM *, tmp_b, num); for (i = 0; i < num; i++) { VALUE item = RARRAY_AREF(arg1, i); bignums[i] = GetBNPtr(item); rb_ary_push(bns_tmp, item); } points = ALLOCV_N(const EC_POINT *, tmp_p, num); points[0] = point_self; /* self */ for (i = 0; i < num - 1; i++) SafeGetECPoint(RARRAY_AREF(arg2, i), points[i + 1]); if (!NIL_P(arg3)) bn_g = GetBNPtr(arg3); if (EC_POINTs_mul(group, point_result, bn_g, num, points, bignums, ossl_bn_ctx) != 1) { ALLOCV_END(tmp_b); ALLOCV_END(tmp_p); ossl_raise(eEC_POINT, NULL); } ALLOCV_END(tmp_b); ALLOCV_END(tmp_p); } return result; } void Init_ossl_ec(void) { #undef rb_intern #if 0 mPKey = rb_define_module_under(mOSSL, "PKey"); cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); #endif eECError = rb_define_class_under(mPKey, "ECError", ePKeyError); /* * Document-class: OpenSSL::PKey::EC * * OpenSSL::PKey::EC provides access to Elliptic Curve Digital Signature * Algorithm (ECDSA) and Elliptic Curve Diffie-Hellman (ECDH). * * === Key exchange * ec1 = OpenSSL::PKey::EC.generate("prime256v1") * ec2 = OpenSSL::PKey::EC.generate("prime256v1") * # ec1 and ec2 have own private key respectively * shared_key1 = ec1.dh_compute_key(ec2.public_key) * shared_key2 = ec2.dh_compute_key(ec1.public_key) * * p shared_key1 == shared_key2 #=> true */ cEC = rb_define_class_under(mPKey, "EC", cPKey); cEC_GROUP = rb_define_class_under(cEC, "Group", rb_cObject); cEC_POINT = rb_define_class_under(cEC, "Point", rb_cObject); eEC_GROUP = rb_define_class_under(cEC_GROUP, "Error", eOSSLError); eEC_POINT = rb_define_class_under(cEC_POINT, "Error", eOSSLError); s_GFp = rb_intern("GFp"); s_GF2m = rb_intern("GF2m"); s_GFp_simple = rb_intern("GFp_simple"); s_GFp_mont = rb_intern("GFp_mont"); s_GFp_nist = rb_intern("GFp_nist"); s_GF2m_simple = rb_intern("GF2m_simple"); ID_uncompressed = rb_intern("uncompressed"); ID_compressed = rb_intern("compressed"); ID_hybrid = rb_intern("hybrid"); rb_define_const(cEC, "NAMED_CURVE", INT2NUM(OPENSSL_EC_NAMED_CURVE)); #if defined(OPENSSL_EC_EXPLICIT_CURVE) rb_define_const(cEC, "EXPLICIT_CURVE", INT2NUM(OPENSSL_EC_EXPLICIT_CURVE)); #endif rb_define_singleton_method(cEC, "builtin_curves", ossl_s_builtin_curves, 0); rb_define_singleton_method(cEC, "generate", ossl_ec_key_s_generate, 1); rb_define_method(cEC, "initialize", ossl_ec_key_initialize, -1); rb_define_copy_func(cEC, ossl_ec_key_initialize_copy); /* copy/dup/cmp */ rb_define_method(cEC, "group", ossl_ec_key_get_group, 0); rb_define_method(cEC, "group=", ossl_ec_key_set_group, 1); rb_define_method(cEC, "private_key", ossl_ec_key_get_private_key, 0); rb_define_method(cEC, "private_key=", ossl_ec_key_set_private_key, 1); rb_define_method(cEC, "public_key", ossl_ec_key_get_public_key, 0); rb_define_method(cEC, "public_key=", ossl_ec_key_set_public_key, 1); rb_define_method(cEC, "private?", ossl_ec_key_is_private, 0); rb_define_method(cEC, "public?", ossl_ec_key_is_public, 0); rb_define_alias(cEC, "private_key?", "private?"); rb_define_alias(cEC, "public_key?", "public?"); /* rb_define_method(cEC, "", ossl_ec_key_get_, 0); rb_define_method(cEC, "=", ossl_ec_key_set_ 1); set/get enc_flags set/get _conv_from set/get asn1_flag (can use ruby to call self.group.asn1_flag) set/get precompute_mult */ rb_define_method(cEC, "generate_key!", ossl_ec_key_generate_key, 0); rb_define_alias(cEC, "generate_key", "generate_key!"); rb_define_method(cEC, "check_key", ossl_ec_key_check_key, 0); rb_define_method(cEC, "dh_compute_key", ossl_ec_key_dh_compute_key, 1); rb_define_method(cEC, "dsa_sign_asn1", ossl_ec_key_dsa_sign_asn1, 1); rb_define_method(cEC, "dsa_verify_asn1", ossl_ec_key_dsa_verify_asn1, 2); /* do_sign/do_verify */ rb_define_method(cEC, "export", ossl_ec_key_export, -1); rb_define_alias(cEC, "to_pem", "export"); rb_define_method(cEC, "to_der", ossl_ec_key_to_der, 0); rb_define_method(cEC, "to_text", ossl_ec_key_to_text, 0); rb_define_alloc_func(cEC_GROUP, ossl_ec_group_alloc); rb_define_method(cEC_GROUP, "initialize", ossl_ec_group_initialize, -1); rb_define_copy_func(cEC_GROUP, ossl_ec_group_initialize_copy); rb_define_method(cEC_GROUP, "eql?", ossl_ec_group_eql, 1); rb_define_alias(cEC_GROUP, "==", "eql?"); /* copy/dup/cmp */ rb_define_method(cEC_GROUP, "generator", ossl_ec_group_get_generator, 0); rb_define_method(cEC_GROUP, "set_generator", ossl_ec_group_set_generator, 3); rb_define_method(cEC_GROUP, "order", ossl_ec_group_get_order, 0); rb_define_method(cEC_GROUP, "cofactor", ossl_ec_group_get_cofactor, 0); rb_define_method(cEC_GROUP, "curve_name", ossl_ec_group_get_curve_name, 0); /* rb_define_method(cEC_GROUP, "curve_name=", ossl_ec_group_set_curve_name, 1); */ rb_define_method(cEC_GROUP, "asn1_flag", ossl_ec_group_get_asn1_flag, 0); rb_define_method(cEC_GROUP, "asn1_flag=", ossl_ec_group_set_asn1_flag, 1); rb_define_method(cEC_GROUP, "point_conversion_form", ossl_ec_group_get_point_conversion_form, 0); rb_define_method(cEC_GROUP, "point_conversion_form=", ossl_ec_group_set_point_conversion_form, 1); rb_define_method(cEC_GROUP, "seed", ossl_ec_group_get_seed, 0); rb_define_method(cEC_GROUP, "seed=", ossl_ec_group_set_seed, 1); /* get/set GFp, GF2m */ rb_define_method(cEC_GROUP, "degree", ossl_ec_group_get_degree, 0); /* check* */ rb_define_method(cEC_GROUP, "to_pem", ossl_ec_group_to_pem, 0); rb_define_method(cEC_GROUP, "to_der", ossl_ec_group_to_der, 0); rb_define_method(cEC_GROUP, "to_text", ossl_ec_group_to_text, 0); rb_define_alloc_func(cEC_POINT, ossl_ec_point_alloc); rb_define_method(cEC_POINT, "initialize", ossl_ec_point_initialize, -1); rb_define_copy_func(cEC_POINT, ossl_ec_point_initialize_copy); rb_attr(cEC_POINT, rb_intern("group"), 1, 0, 0); rb_define_method(cEC_POINT, "eql?", ossl_ec_point_eql, 1); rb_define_alias(cEC_POINT, "==", "eql?"); rb_define_method(cEC_POINT, "infinity?", ossl_ec_point_is_at_infinity, 0); rb_define_method(cEC_POINT, "on_curve?", ossl_ec_point_is_on_curve, 0); rb_define_method(cEC_POINT, "make_affine!", ossl_ec_point_make_affine, 0); rb_define_method(cEC_POINT, "invert!", ossl_ec_point_invert, 0); rb_define_method(cEC_POINT, "set_to_infinity!", ossl_ec_point_set_to_infinity, 0); /* all the other methods */ rb_define_method(cEC_POINT, "to_bn", ossl_ec_point_to_bn, -1); rb_define_method(cEC_POINT, "mul", ossl_ec_point_mul, -1); id_i_group = rb_intern("@group"); } #else /* defined NO_EC */ void Init_ossl_ec(void) { } #endif /* NO_EC */ openssl-2.0.9/ext/openssl/ossl_pkey_rsa.c000066400000000000000000000456721336157045000205330ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #if !defined(OPENSSL_NO_RSA) #define GetPKeyRSA(obj, pkey) do { \ GetPKey((obj), (pkey)); \ if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) { /* PARANOIA? */ \ ossl_raise(rb_eRuntimeError, "THIS IS NOT A RSA!") ; \ } \ } while (0) #define GetRSA(obj, rsa) do { \ EVP_PKEY *_pkey; \ GetPKeyRSA((obj), _pkey); \ (rsa) = EVP_PKEY_get0_RSA(_pkey); \ } while (0) static inline int RSA_HAS_PRIVATE(RSA *rsa) { const BIGNUM *p, *q; RSA_get0_factors(rsa, &p, &q); return p && q; /* d? why? */ } static inline int RSA_PRIVATE(VALUE obj, RSA *rsa) { return RSA_HAS_PRIVATE(rsa) || OSSL_PKEY_IS_PRIVATE(obj); } /* * Classes */ VALUE cRSA; VALUE eRSAError; /* * Public */ static VALUE rsa_instance(VALUE klass, RSA *rsa) { EVP_PKEY *pkey; VALUE obj; if (!rsa) { return Qfalse; } obj = NewPKey(klass); if (!(pkey = EVP_PKEY_new())) { return Qfalse; } if (!EVP_PKEY_assign_RSA(pkey, rsa)) { EVP_PKEY_free(pkey); return Qfalse; } SetPKey(obj, pkey); return obj; } VALUE ossl_rsa_new(EVP_PKEY *pkey) { VALUE obj; if (!pkey) { obj = rsa_instance(cRSA, RSA_new()); } else { obj = NewPKey(cRSA); if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) { ossl_raise(rb_eTypeError, "Not a RSA key!"); } SetPKey(obj, pkey); } if (obj == Qfalse) { ossl_raise(eRSAError, NULL); } return obj; } /* * Private */ struct rsa_blocking_gen_arg { RSA *rsa; BIGNUM *e; int size; BN_GENCB *cb; int result; }; static void * rsa_blocking_gen(void *arg) { struct rsa_blocking_gen_arg *gen = (struct rsa_blocking_gen_arg *)arg; gen->result = RSA_generate_key_ex(gen->rsa, gen->size, gen->e, gen->cb); return 0; } static RSA * rsa_generate(int size, unsigned long exp) { int i; struct ossl_generate_cb_arg cb_arg = { 0 }; struct rsa_blocking_gen_arg gen_arg; RSA *rsa = RSA_new(); BIGNUM *e = BN_new(); BN_GENCB *cb = BN_GENCB_new(); if (!rsa || !e || !cb) { RSA_free(rsa); BN_free(e); BN_GENCB_free(cb); return NULL; } for (i = 0; i < (int)sizeof(exp) * 8; ++i) { if (exp & (1UL << i)) { if (BN_set_bit(e, i) == 0) { BN_free(e); RSA_free(rsa); BN_GENCB_free(cb); return NULL; } } } if (rb_block_given_p()) cb_arg.yield = 1; BN_GENCB_set(cb, ossl_generate_cb_2, &cb_arg); gen_arg.rsa = rsa; gen_arg.e = e; gen_arg.size = size; gen_arg.cb = cb; if (cb_arg.yield == 1) { /* we cannot release GVL when callback proc is supplied */ rsa_blocking_gen(&gen_arg); } else { /* there's a chance to unblock */ rb_thread_call_without_gvl(rsa_blocking_gen, &gen_arg, ossl_generate_cb_stop, &cb_arg); } BN_GENCB_free(cb); BN_free(e); if (!gen_arg.result) { RSA_free(rsa); if (cb_arg.state) { /* must clear OpenSSL error stack */ ossl_clear_error(); rb_jump_tag(cb_arg.state); } return NULL; } return rsa; } /* * call-seq: * RSA.generate(size) => RSA instance * RSA.generate(size, exponent) => RSA instance * * Generates an RSA keypair. +size+ is an integer representing the desired key * size. Keys smaller than 1024 should be considered insecure. +exponent+ is * an odd number normally 3, 17, or 65537. */ static VALUE ossl_rsa_s_generate(int argc, VALUE *argv, VALUE klass) { /* why does this method exist? why can't initialize take an optional exponent? */ RSA *rsa; VALUE size, exp; VALUE obj; rb_scan_args(argc, argv, "11", &size, &exp); rsa = rsa_generate(NUM2INT(size), NIL_P(exp) ? RSA_F4 : NUM2ULONG(exp)); /* err handled by rsa_instance */ obj = rsa_instance(klass, rsa); if (obj == Qfalse) { RSA_free(rsa); ossl_raise(eRSAError, NULL); } return obj; } /* * call-seq: * RSA.new(key_size) => RSA instance * RSA.new(encoded_key) => RSA instance * RSA.new(encoded_key, pass_phrase) => RSA instance * * Generates or loads an RSA keypair. If an integer +key_size+ is given it * represents the desired key size. Keys less than 1024 bits should be * considered insecure. * * A key can instead be loaded from an +encoded_key+ which must be PEM or DER * encoded. A +pass_phrase+ can be used to decrypt the key. If none is given * OpenSSL will prompt for the pass phrase. * * = Examples * * OpenSSL::PKey::RSA.new 2048 * OpenSSL::PKey::RSA.new File.read 'rsa.pem' * OpenSSL::PKey::RSA.new File.read('rsa.pem'), 'my pass phrase' */ static VALUE ossl_rsa_initialize(int argc, VALUE *argv, VALUE self) { EVP_PKEY *pkey; RSA *rsa; BIO *in; VALUE arg, pass; GetPKey(self, pkey); if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) { rsa = RSA_new(); } else if (RB_INTEGER_TYPE_P(arg)) { rsa = rsa_generate(NUM2INT(arg), NIL_P(pass) ? RSA_F4 : NUM2ULONG(pass)); if (!rsa) ossl_raise(eRSAError, NULL); } else { pass = ossl_pem_passwd_value(pass); arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); rsa = PEM_read_bio_RSAPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass); if (!rsa) { OSSL_BIO_reset(in); rsa = PEM_read_bio_RSA_PUBKEY(in, NULL, NULL, NULL); } if (!rsa) { OSSL_BIO_reset(in); rsa = d2i_RSAPrivateKey_bio(in, NULL); } if (!rsa) { OSSL_BIO_reset(in); rsa = d2i_RSA_PUBKEY_bio(in, NULL); } if (!rsa) { OSSL_BIO_reset(in); rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL); } if (!rsa) { OSSL_BIO_reset(in); rsa = d2i_RSAPublicKey_bio(in, NULL); } BIO_free(in); if (!rsa) { ossl_raise(eRSAError, "Neither PUB key nor PRIV key"); } } if (!EVP_PKEY_assign_RSA(pkey, rsa)) { RSA_free(rsa); ossl_raise(eRSAError, NULL); } return self; } static VALUE ossl_rsa_initialize_copy(VALUE self, VALUE other) { EVP_PKEY *pkey; RSA *rsa, *rsa_new; GetPKey(self, pkey); if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE) ossl_raise(eRSAError, "RSA already initialized"); GetRSA(other, rsa); rsa_new = ASN1_dup((i2d_of_void *)i2d_RSAPrivateKey, (d2i_of_void *)d2i_RSAPrivateKey, (char *)rsa); if (!rsa_new) ossl_raise(eRSAError, "ASN1_dup"); EVP_PKEY_assign_RSA(pkey, rsa_new); return self; } /* * call-seq: * rsa.public? => true * * The return value is always true since every private key is also a public * key. */ static VALUE ossl_rsa_is_public(VALUE self) { RSA *rsa; GetRSA(self, rsa); /* * This method should check for n and e. BUG. */ (void)rsa; return Qtrue; } /* * call-seq: * rsa.private? => true | false * * Does this keypair contain a private key? */ static VALUE ossl_rsa_is_private(VALUE self) { RSA *rsa; GetRSA(self, rsa); return RSA_PRIVATE(self, rsa) ? Qtrue : Qfalse; } /* * call-seq: * rsa.export([cipher, pass_phrase]) => PEM-format String * rsa.to_pem([cipher, pass_phrase]) => PEM-format String * rsa.to_s([cipher, pass_phrase]) => PEM-format String * * Outputs this keypair in PEM encoding. If +cipher+ and +pass_phrase+ are * given they will be used to encrypt the key. +cipher+ must be an * OpenSSL::Cipher instance. */ static VALUE ossl_rsa_export(int argc, VALUE *argv, VALUE self) { RSA *rsa; BIO *out; const EVP_CIPHER *ciph = NULL; VALUE cipher, pass, str; GetRSA(self, rsa); rb_scan_args(argc, argv, "02", &cipher, &pass); if (!NIL_P(cipher)) { ciph = GetCipherPtr(cipher); pass = ossl_pem_passwd_value(pass); } if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eRSAError, NULL); } if (RSA_HAS_PRIVATE(rsa)) { if (!PEM_write_bio_RSAPrivateKey(out, rsa, ciph, NULL, 0, ossl_pem_passwd_cb, (void *)pass)) { BIO_free(out); ossl_raise(eRSAError, NULL); } } else { if (!PEM_write_bio_RSA_PUBKEY(out, rsa)) { BIO_free(out); ossl_raise(eRSAError, NULL); } } str = ossl_membio2str(out); return str; } /* * call-seq: * rsa.to_der => DER-format String * * Outputs this keypair in DER encoding. */ static VALUE ossl_rsa_to_der(VALUE self) { RSA *rsa; int (*i2d_func)(const RSA *, unsigned char **); unsigned char *p; long len; VALUE str; GetRSA(self, rsa); if (RSA_HAS_PRIVATE(rsa)) i2d_func = i2d_RSAPrivateKey; else i2d_func = (int (*)(const RSA *, unsigned char **))i2d_RSA_PUBKEY; if((len = i2d_func(rsa, NULL)) <= 0) ossl_raise(eRSAError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_func(rsa, &p) < 0) ossl_raise(eRSAError, NULL); ossl_str_adjust(str, p); return str; } /* * call-seq: * rsa.public_encrypt(string) => String * rsa.public_encrypt(string, padding) => String * * Encrypt +string+ with the public key. +padding+ defaults to PKCS1_PADDING. * The encrypted string output can be decrypted using #private_decrypt. */ static VALUE ossl_rsa_public_encrypt(int argc, VALUE *argv, VALUE self) { RSA *rsa; const BIGNUM *rsa_n; int buf_len, pad; VALUE str, buffer, padding; GetRSA(self, rsa); RSA_get0_key(rsa, &rsa_n, NULL, NULL); if (!rsa_n) ossl_raise(eRSAError, "incomplete RSA"); rb_scan_args(argc, argv, "11", &buffer, &padding); pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding); StringValue(buffer); str = rb_str_new(0, RSA_size(rsa)); buf_len = RSA_public_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer), (unsigned char *)RSTRING_PTR(str), rsa, pad); if (buf_len < 0) ossl_raise(eRSAError, NULL); rb_str_set_len(str, buf_len); return str; } /* * call-seq: * rsa.public_decrypt(string) => String * rsa.public_decrypt(string, padding) => String * * Decrypt +string+, which has been encrypted with the private key, with the * public key. +padding+ defaults to PKCS1_PADDING. */ static VALUE ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self) { RSA *rsa; const BIGNUM *rsa_n; int buf_len, pad; VALUE str, buffer, padding; GetRSA(self, rsa); RSA_get0_key(rsa, &rsa_n, NULL, NULL); if (!rsa_n) ossl_raise(eRSAError, "incomplete RSA"); rb_scan_args(argc, argv, "11", &buffer, &padding); pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding); StringValue(buffer); str = rb_str_new(0, RSA_size(rsa)); buf_len = RSA_public_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer), (unsigned char *)RSTRING_PTR(str), rsa, pad); if (buf_len < 0) ossl_raise(eRSAError, NULL); rb_str_set_len(str, buf_len); return str; } /* * call-seq: * rsa.private_encrypt(string) => String * rsa.private_encrypt(string, padding) => String * * Encrypt +string+ with the private key. +padding+ defaults to PKCS1_PADDING. * The encrypted string output can be decrypted using #public_decrypt. */ static VALUE ossl_rsa_private_encrypt(int argc, VALUE *argv, VALUE self) { RSA *rsa; const BIGNUM *rsa_n; int buf_len, pad; VALUE str, buffer, padding; GetRSA(self, rsa); RSA_get0_key(rsa, &rsa_n, NULL, NULL); if (!rsa_n) ossl_raise(eRSAError, "incomplete RSA"); if (!RSA_PRIVATE(self, rsa)) ossl_raise(eRSAError, "private key needed."); rb_scan_args(argc, argv, "11", &buffer, &padding); pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding); StringValue(buffer); str = rb_str_new(0, RSA_size(rsa)); buf_len = RSA_private_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer), (unsigned char *)RSTRING_PTR(str), rsa, pad); if (buf_len < 0) ossl_raise(eRSAError, NULL); rb_str_set_len(str, buf_len); return str; } /* * call-seq: * rsa.private_decrypt(string) => String * rsa.private_decrypt(string, padding) => String * * Decrypt +string+, which has been encrypted with the public key, with the * private key. +padding+ defaults to PKCS1_PADDING. */ static VALUE ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self) { RSA *rsa; const BIGNUM *rsa_n; int buf_len, pad; VALUE str, buffer, padding; GetRSA(self, rsa); RSA_get0_key(rsa, &rsa_n, NULL, NULL); if (!rsa_n) ossl_raise(eRSAError, "incomplete RSA"); if (!RSA_PRIVATE(self, rsa)) ossl_raise(eRSAError, "private key needed."); rb_scan_args(argc, argv, "11", &buffer, &padding); pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding); StringValue(buffer); str = rb_str_new(0, RSA_size(rsa)); buf_len = RSA_private_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer), (unsigned char *)RSTRING_PTR(str), rsa, pad); if (buf_len < 0) ossl_raise(eRSAError, NULL); rb_str_set_len(str, buf_len); return str; } /* * call-seq: * rsa.params => hash * * THIS METHOD IS INSECURE, PRIVATE INFORMATION CAN LEAK OUT!!! * * Stores all parameters of key to the hash. The hash has keys 'n', 'e', 'd', * 'p', 'q', 'dmp1', 'dmq1', 'iqmp'. * * Don't use :-)) (It's up to you) */ static VALUE ossl_rsa_get_params(VALUE self) { RSA *rsa; VALUE hash; const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; GetRSA(self, rsa); RSA_get0_key(rsa, &n, &e, &d); RSA_get0_factors(rsa, &p, &q); RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); hash = rb_hash_new(); rb_hash_aset(hash, rb_str_new2("n"), ossl_bn_new(n)); rb_hash_aset(hash, rb_str_new2("e"), ossl_bn_new(e)); rb_hash_aset(hash, rb_str_new2("d"), ossl_bn_new(d)); rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(p)); rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(q)); rb_hash_aset(hash, rb_str_new2("dmp1"), ossl_bn_new(dmp1)); rb_hash_aset(hash, rb_str_new2("dmq1"), ossl_bn_new(dmq1)); rb_hash_aset(hash, rb_str_new2("iqmp"), ossl_bn_new(iqmp)); return hash; } /* * call-seq: * rsa.to_text => String * * THIS METHOD IS INSECURE, PRIVATE INFORMATION CAN LEAK OUT!!! * * Dumps all parameters of a keypair to a String * * Don't use :-)) (It's up to you) */ static VALUE ossl_rsa_to_text(VALUE self) { RSA *rsa; BIO *out; VALUE str; GetRSA(self, rsa); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eRSAError, NULL); } if (!RSA_print(out, rsa, 0)) { /* offset = 0 */ BIO_free(out); ossl_raise(eRSAError, NULL); } str = ossl_membio2str(out); return str; } /* * call-seq: * rsa.public_key -> RSA * * Makes new RSA instance containing the public key from the private key. */ static VALUE ossl_rsa_to_public_key(VALUE self) { EVP_PKEY *pkey; RSA *rsa; VALUE obj; GetPKeyRSA(self, pkey); /* err check performed by rsa_instance */ rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(pkey)); obj = rsa_instance(rb_obj_class(self), rsa); if (obj == Qfalse) { RSA_free(rsa); ossl_raise(eRSAError, NULL); } return obj; } /* * TODO: Test me static VALUE ossl_rsa_blinding_on(VALUE self) { RSA *rsa; GetRSA(self, rsa); if (RSA_blinding_on(rsa, ossl_bn_ctx) != 1) { ossl_raise(eRSAError, NULL); } return self; } static VALUE ossl_rsa_blinding_off(VALUE self) { RSA *rsa; GetRSA(self, rsa); RSA_blinding_off(rsa); return self; } */ /* * Document-method: OpenSSL::PKey::RSA#set_key * call-seq: * rsa.set_key(n, e, d) -> self * * Sets +n+, +e+, +d+ for the RSA instance. */ OSSL_PKEY_BN_DEF3(rsa, RSA, key, n, e, d) /* * Document-method: OpenSSL::PKey::RSA#set_factors * call-seq: * rsa.set_factors(p, q) -> self * * Sets +p+, +q+ for the RSA instance. */ OSSL_PKEY_BN_DEF2(rsa, RSA, factors, p, q) /* * Document-method: OpenSSL::PKey::RSA#set_crt_params * call-seq: * rsa.set_crt_params(dmp1, dmq1, iqmp) -> self * * Sets +dmp1+, +dmq1+, +iqmp+ for the RSA instance. They are calculated by * d mod (p - 1), d mod (q - 1) and q^(-1) mod p * respectively. */ OSSL_PKEY_BN_DEF3(rsa, RSA, crt_params, dmp1, dmq1, iqmp) /* * INIT */ #define DefRSAConst(x) rb_define_const(cRSA, #x, INT2NUM(RSA_##x)) void Init_ossl_rsa(void) { #if 0 mPKey = rb_define_module_under(mOSSL, "PKey"); cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject); ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError); #endif /* Document-class: OpenSSL::PKey::RSAError * * Generic exception that is raised if an operation on an RSA PKey * fails unexpectedly or in case an instantiation of an instance of RSA * fails due to non-conformant input data. */ eRSAError = rb_define_class_under(mPKey, "RSAError", ePKeyError); /* Document-class: OpenSSL::PKey::RSA * * RSA is an asymmetric public key algorithm that has been formalized in * RFC 3447. It is in widespread use in public key infrastructures (PKI) * where certificates (cf. OpenSSL::X509::Certificate) often are issued * on the basis of a public/private RSA key pair. RSA is used in a wide * field of applications such as secure (symmetric) key exchange, e.g. * when establishing a secure TLS/SSL connection. It is also used in * various digital signature schemes. */ cRSA = rb_define_class_under(mPKey, "RSA", cPKey); rb_define_singleton_method(cRSA, "generate", ossl_rsa_s_generate, -1); rb_define_method(cRSA, "initialize", ossl_rsa_initialize, -1); rb_define_copy_func(cRSA, ossl_rsa_initialize_copy); rb_define_method(cRSA, "public?", ossl_rsa_is_public, 0); rb_define_method(cRSA, "private?", ossl_rsa_is_private, 0); rb_define_method(cRSA, "to_text", ossl_rsa_to_text, 0); rb_define_method(cRSA, "export", ossl_rsa_export, -1); rb_define_alias(cRSA, "to_pem", "export"); rb_define_alias(cRSA, "to_s", "export"); rb_define_method(cRSA, "to_der", ossl_rsa_to_der, 0); rb_define_method(cRSA, "public_key", ossl_rsa_to_public_key, 0); rb_define_method(cRSA, "public_encrypt", ossl_rsa_public_encrypt, -1); rb_define_method(cRSA, "public_decrypt", ossl_rsa_public_decrypt, -1); rb_define_method(cRSA, "private_encrypt", ossl_rsa_private_encrypt, -1); rb_define_method(cRSA, "private_decrypt", ossl_rsa_private_decrypt, -1); DEF_OSSL_PKEY_BN(cRSA, rsa, n); DEF_OSSL_PKEY_BN(cRSA, rsa, e); DEF_OSSL_PKEY_BN(cRSA, rsa, d); DEF_OSSL_PKEY_BN(cRSA, rsa, p); DEF_OSSL_PKEY_BN(cRSA, rsa, q); DEF_OSSL_PKEY_BN(cRSA, rsa, dmp1); DEF_OSSL_PKEY_BN(cRSA, rsa, dmq1); DEF_OSSL_PKEY_BN(cRSA, rsa, iqmp); rb_define_method(cRSA, "set_key", ossl_rsa_set_key, 3); rb_define_method(cRSA, "set_factors", ossl_rsa_set_factors, 2); rb_define_method(cRSA, "set_crt_params", ossl_rsa_set_crt_params, 3); rb_define_method(cRSA, "params", ossl_rsa_get_params, 0); DefRSAConst(PKCS1_PADDING); DefRSAConst(SSLV23_PADDING); DefRSAConst(NO_PADDING); DefRSAConst(PKCS1_OAEP_PADDING); /* * TODO: Test it rb_define_method(cRSA, "blinding_on!", ossl_rsa_blinding_on, 0); rb_define_method(cRSA, "blinding_off!", ossl_rsa_blinding_off, 0); */ } #else /* defined NO_RSA */ void Init_ossl_rsa(void) { } #endif /* NO_RSA */ openssl-2.0.9/ext/openssl/ossl_rand.c000066400000000000000000000126771336157045000176410ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * * All rights reserved. * * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" VALUE mRandom; VALUE eRandomError; /* * call-seq: * seed(str) -> str * * ::seed is equivalent to ::add where +entropy+ is length of +str+. */ static VALUE ossl_rand_seed(VALUE self, VALUE str) { StringValue(str); RAND_seed(RSTRING_PTR(str), RSTRING_LENINT(str)); return str; } /* * call-seq: * add(str, entropy) -> self * * Mixes the bytes from +str+ into the Pseudo Random Number Generator(PRNG) * state. * * Thus, if the data from +str+ are unpredictable to an adversary, this * increases the uncertainty about the state and makes the PRNG output less * predictable. * * The +entropy+ argument is (the lower bound of) an estimate of how much * randomness is contained in +str+, measured in bytes. * * === Example * * pid = $$ * now = Time.now * ary = [now.to_i, now.nsec, 1000, pid] * OpenSSL::Random.add(ary.join, 0.0) * OpenSSL::Random.seed(ary.join) */ static VALUE ossl_rand_add(VALUE self, VALUE str, VALUE entropy) { StringValue(str); RAND_add(RSTRING_PTR(str), RSTRING_LENINT(str), NUM2DBL(entropy)); return self; } /* * call-seq: * load_random_file(filename) -> true * * Reads bytes from +filename+ and adds them to the PRNG. */ static VALUE ossl_rand_load_file(VALUE self, VALUE filename) { rb_check_safe_obj(filename); if(!RAND_load_file(StringValueCStr(filename), -1)) { ossl_raise(eRandomError, NULL); } return Qtrue; } /* * call-seq: * write_random_file(filename) -> true * * Writes a number of random generated bytes (currently 1024) to +filename+ * which can be used to initialize the PRNG by calling ::load_random_file in a * later session. */ static VALUE ossl_rand_write_file(VALUE self, VALUE filename) { rb_check_safe_obj(filename); if (RAND_write_file(StringValueCStr(filename)) == -1) { ossl_raise(eRandomError, NULL); } return Qtrue; } /* * call-seq: * random_bytes(length) -> string * * Generates +string+ with +length+ number of cryptographically strong * pseudo-random bytes. * * === Example * * OpenSSL::Random.random_bytes(12) * #=> "..." */ static VALUE ossl_rand_bytes(VALUE self, VALUE len) { VALUE str; int n = NUM2INT(len); int ret; str = rb_str_new(0, n); ret = RAND_bytes((unsigned char *)RSTRING_PTR(str), n); if (ret == 0) { ossl_raise(eRandomError, "RAND_bytes"); } else if (ret == -1) { ossl_raise(eRandomError, "RAND_bytes is not supported"); } return str; } #if defined(HAVE_RAND_PSEUDO_BYTES) /* * call-seq: * pseudo_bytes(length) -> string * * Generates +string+ with +length+ number of pseudo-random bytes. * * Pseudo-random byte sequences generated by ::pseudo_bytes will be unique if * they are of sufficient length, but are not necessarily unpredictable. * * === Example * * OpenSSL::Random.pseudo_bytes(12) * #=> "..." */ static VALUE ossl_rand_pseudo_bytes(VALUE self, VALUE len) { VALUE str; int n = NUM2INT(len); str = rb_str_new(0, n); if (RAND_pseudo_bytes((unsigned char *)RSTRING_PTR(str), n) < 1) { ossl_raise(eRandomError, NULL); } return str; } #endif #ifdef HAVE_RAND_EGD /* * call-seq: * egd(filename) -> true * * Same as ::egd_bytes but queries 255 bytes by default. */ static VALUE ossl_rand_egd(VALUE self, VALUE filename) { rb_check_safe_obj(filename); if (RAND_egd(StringValueCStr(filename)) == -1) { ossl_raise(eRandomError, NULL); } return Qtrue; } /* * call-seq: * egd_bytes(filename, length) -> true * * Queries the entropy gathering daemon EGD on socket path given by +filename+. * * Fetches +length+ number of bytes and uses ::add to seed the OpenSSL built-in * PRNG. */ static VALUE ossl_rand_egd_bytes(VALUE self, VALUE filename, VALUE len) { int n = NUM2INT(len); rb_check_safe_obj(filename); if (RAND_egd_bytes(StringValueCStr(filename), n) == -1) { ossl_raise(eRandomError, NULL); } return Qtrue; } #endif /* HAVE_RAND_EGD */ /* * call-seq: * status? => true | false * * Return true if the PRNG has been seeded with enough data, false otherwise. */ static VALUE ossl_rand_status(VALUE self) { return RAND_status() ? Qtrue : Qfalse; } /* * INIT */ void Init_ossl_rand(void) { #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif mRandom = rb_define_module_under(mOSSL, "Random"); eRandomError = rb_define_class_under(mRandom, "RandomError", eOSSLError); rb_define_module_function(mRandom, "seed", ossl_rand_seed, 1); rb_define_module_function(mRandom, "random_add", ossl_rand_add, 2); rb_define_module_function(mRandom, "load_random_file", ossl_rand_load_file, 1); rb_define_module_function(mRandom, "write_random_file", ossl_rand_write_file, 1); rb_define_module_function(mRandom, "random_bytes", ossl_rand_bytes, 1); #if defined(HAVE_RAND_PSEUDO_BYTES) rb_define_module_function(mRandom, "pseudo_bytes", ossl_rand_pseudo_bytes, 1); #endif #ifdef HAVE_RAND_EGD rb_define_module_function(mRandom, "egd", ossl_rand_egd, 1); rb_define_module_function(mRandom, "egd_bytes", ossl_rand_egd_bytes, 2); #endif /* HAVE_RAND_EGD */ rb_define_module_function(mRandom, "status?", ossl_rand_status, 0); } openssl-2.0.9/ext/openssl/ossl_rand.h000066400000000000000000000005671336157045000176410ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_RAND_H_) #define _OSSL_RAND_H_ extern VALUE mRandom; extern VALUE eRandomError; void Init_ossl_rand(void); #endif /* _OSSL_RAND_H_ */ openssl-2.0.9/ext/openssl/ossl_ssl.c000066400000000000000000002341011336157045000175020ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2000-2002 GOTOU Yuuzou * Copyright (C) 2001-2002 Michal Rokos * Copyright (C) 2001-2007 Technorama Ltd. * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0])) #ifdef _WIN32 # define TO_SOCKET(s) _get_osfhandle(s) #else # define TO_SOCKET(s) (s) #endif #define GetSSLCTX(obj, ctx) do { \ TypedData_Get_Struct((obj), SSL_CTX, &ossl_sslctx_type, (ctx)); \ } while (0) VALUE mSSL; static VALUE mSSLExtConfig; static VALUE eSSLError; VALUE cSSLContext; VALUE cSSLSocket; static VALUE eSSLErrorWaitReadable; static VALUE eSSLErrorWaitWritable; static ID ID_callback_state, id_tmp_dh_callback, id_tmp_ecdh_callback, id_npn_protocols_encoded; static VALUE sym_exception, sym_wait_readable, sym_wait_writable; static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode, id_i_verify_depth, id_i_verify_callback, id_i_client_ca, id_i_renegotiation_cb, id_i_cert, id_i_key, id_i_extra_chain_cert, id_i_client_cert_cb, id_i_tmp_ecdh_callback, id_i_timeout, id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb, id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols, id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb, id_i_verify_hostname; static ID id_i_io, id_i_context, id_i_hostname; /* * SSLContext class */ static const struct { const char *name; SSL_METHOD *(*func)(void); /* FIXME: constify when dropping 0.9.8 */ int version; } ossl_ssl_method_tab[] = { #if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION) #define OSSL_SSL_METHOD_ENTRY(name, version) \ { #name, (SSL_METHOD *(*)(void))TLS_method, version }, \ { #name"_server", (SSL_METHOD *(*)(void))TLS_server_method, version }, \ { #name"_client", (SSL_METHOD *(*)(void))TLS_client_method, version } #else #define OSSL_SSL_METHOD_ENTRY(name, version) \ { #name, (SSL_METHOD *(*)(void))name##_method, version }, \ { #name"_server", (SSL_METHOD *(*)(void))name##_server_method, version }, \ { #name"_client", (SSL_METHOD *(*)(void))name##_client_method, version } #endif #if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL2_METHOD) && defined(HAVE_SSLV2_METHOD) OSSL_SSL_METHOD_ENTRY(SSLv2, SSL2_VERSION), #endif #if !defined(OPENSSL_NO_SSL3) && !defined(OPENSSL_NO_SSL3_METHOD) && defined(HAVE_SSLV3_METHOD) OSSL_SSL_METHOD_ENTRY(SSLv3, SSL3_VERSION), #endif #if !defined(OPENSSL_NO_TLS1) && !defined(OPENSSL_NO_TLS1_METHOD) OSSL_SSL_METHOD_ENTRY(TLSv1, TLS1_VERSION), #endif #if !defined(OPENSSL_NO_TLS1_1) && !defined(OPENSSL_NO_TLS1_1_METHOD) && defined(HAVE_TLSV1_1_METHOD) OSSL_SSL_METHOD_ENTRY(TLSv1_1, TLS1_1_VERSION), #endif #if !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_TLS1_2_METHOD) && defined(HAVE_TLSV1_2_METHOD) OSSL_SSL_METHOD_ENTRY(TLSv1_2, TLS1_2_VERSION), #endif OSSL_SSL_METHOD_ENTRY(SSLv23, 0), #undef OSSL_SSL_METHOD_ENTRY }; static int ossl_ssl_ex_vcb_idx; static int ossl_ssl_ex_store_p; static int ossl_ssl_ex_ptr_idx; static void ossl_sslctx_free(void *ptr) { SSL_CTX *ctx = ptr; #if !defined(HAVE_X509_STORE_UP_REF) if(ctx && SSL_CTX_get_ex_data(ctx, ossl_ssl_ex_store_p)== (void*)1) ctx->cert_store = NULL; #endif SSL_CTX_free(ctx); } static const rb_data_type_t ossl_sslctx_type = { "OpenSSL/SSL/CTX", { 0, ossl_sslctx_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static VALUE ossl_sslctx_s_alloc(VALUE klass) { SSL_CTX *ctx; long mode = SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER; VALUE obj; #ifdef SSL_MODE_RELEASE_BUFFERS mode |= SSL_MODE_RELEASE_BUFFERS; #endif obj = TypedData_Wrap_Struct(klass, &ossl_sslctx_type, 0); ctx = SSL_CTX_new(SSLv23_method()); if (!ctx) { ossl_raise(eSSLError, "SSL_CTX_new"); } SSL_CTX_set_mode(ctx, mode); RTYPEDDATA_DATA(obj) = ctx; SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_ptr_idx, (void*)obj); #if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_ECDH_AUTO) /* We use SSL_CTX_set1_curves_list() to specify the curve used in ECDH. It * allows to specify multiple curve names and OpenSSL will select * automatically from them. In OpenSSL 1.0.2, the automatic selection has to * be enabled explicitly. But OpenSSL 1.1.0 removed the knob and it is * always enabled. To uniform the behavior, we enable the automatic * selection also in 1.0.2. Users can still disable ECDH by removing ECDH * cipher suites by SSLContext#ciphers=. */ if (!SSL_CTX_set_ecdh_auto(ctx, 1)) ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto"); #endif return obj; } /* * call-seq: * ctx.ssl_version = :TLSv1 * ctx.ssl_version = "SSLv23_client" * * Sets the SSL/TLS protocol version for the context. This forces connections to * use only the specified protocol version. * * You can get a list of valid versions with OpenSSL::SSL::SSLContext::METHODS */ static VALUE ossl_sslctx_set_ssl_version(VALUE self, VALUE ssl_method) { SSL_CTX *ctx; const char *s; VALUE m = ssl_method; int i; GetSSLCTX(self, ctx); if (RB_TYPE_P(ssl_method, T_SYMBOL)) m = rb_sym2str(ssl_method); s = StringValueCStr(m); for (i = 0; i < numberof(ossl_ssl_method_tab); i++) { if (strcmp(ossl_ssl_method_tab[i].name, s) == 0) { #if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION) int version = ossl_ssl_method_tab[i].version; #endif SSL_METHOD *method = ossl_ssl_method_tab[i].func(); if (SSL_CTX_set_ssl_version(ctx, method) != 1) ossl_raise(eSSLError, "SSL_CTX_set_ssl_version"); #if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION) if (!SSL_CTX_set_min_proto_version(ctx, version)) ossl_raise(eSSLError, "SSL_CTX_set_min_proto_version"); if (!SSL_CTX_set_max_proto_version(ctx, version)) ossl_raise(eSSLError, "SSL_CTX_set_max_proto_version"); #endif return ssl_method; } } ossl_raise(rb_eArgError, "unknown SSL method `%"PRIsVALUE"'.", m); } static VALUE ossl_call_client_cert_cb(VALUE obj) { VALUE ctx_obj, cb, ary, cert, key; ctx_obj = rb_attr_get(obj, id_i_context); cb = rb_attr_get(ctx_obj, id_i_client_cert_cb); if (NIL_P(cb)) return Qnil; ary = rb_funcall(cb, rb_intern("call"), 1, obj); Check_Type(ary, T_ARRAY); GetX509CertPtr(cert = rb_ary_entry(ary, 0)); GetPrivPKeyPtr(key = rb_ary_entry(ary, 1)); return rb_ary_new3(2, cert, key); } static int ossl_client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey) { VALUE obj, ret; obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); ret = rb_protect(ossl_call_client_cert_cb, obj, NULL); if (NIL_P(ret)) return 0; *x509 = DupX509CertPtr(RARRAY_AREF(ret, 0)); *pkey = DupPKeyPtr(RARRAY_AREF(ret, 1)); return 1; } #if !defined(OPENSSL_NO_DH) || \ !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) struct tmp_dh_callback_args { VALUE ssl_obj; ID id; int type; int is_export; int keylength; }; static EVP_PKEY * ossl_call_tmp_dh_callback(struct tmp_dh_callback_args *args) { VALUE cb, dh; EVP_PKEY *pkey; cb = rb_funcall(args->ssl_obj, args->id, 0); if (NIL_P(cb)) return NULL; dh = rb_funcall(cb, rb_intern("call"), 3, args->ssl_obj, INT2NUM(args->is_export), INT2NUM(args->keylength)); pkey = GetPKeyPtr(dh); if (EVP_PKEY_base_id(pkey) != args->type) return NULL; return pkey; } #endif #if !defined(OPENSSL_NO_DH) static DH * ossl_tmp_dh_callback(SSL *ssl, int is_export, int keylength) { VALUE rb_ssl; EVP_PKEY *pkey; struct tmp_dh_callback_args args; int state; rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); args.ssl_obj = rb_ssl; args.id = id_tmp_dh_callback; args.is_export = is_export; args.keylength = keylength; args.type = EVP_PKEY_DH; pkey = (EVP_PKEY *)rb_protect((VALUE (*)(VALUE))ossl_call_tmp_dh_callback, (VALUE)&args, &state); if (state) { rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state)); return NULL; } if (!pkey) return NULL; return EVP_PKEY_get0_DH(pkey); } #endif /* OPENSSL_NO_DH */ #if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) static EC_KEY * ossl_tmp_ecdh_callback(SSL *ssl, int is_export, int keylength) { VALUE rb_ssl; EVP_PKEY *pkey; struct tmp_dh_callback_args args; int state; rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); args.ssl_obj = rb_ssl; args.id = id_tmp_ecdh_callback; args.is_export = is_export; args.keylength = keylength; args.type = EVP_PKEY_EC; pkey = (EVP_PKEY *)rb_protect((VALUE (*)(VALUE))ossl_call_tmp_dh_callback, (VALUE)&args, &state); if (state) { rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state)); return NULL; } if (!pkey) return NULL; return EVP_PKEY_get0_EC_KEY(pkey); } #endif static VALUE call_verify_certificate_identity(VALUE ctx_v) { X509_STORE_CTX *ctx = (X509_STORE_CTX *)ctx_v; SSL *ssl; VALUE ssl_obj, hostname, cert_obj; ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); hostname = rb_attr_get(ssl_obj, id_i_hostname); if (!RTEST(hostname)) { rb_warning("verify_hostname requires hostname to be set"); return Qtrue; } cert_obj = ossl_x509_new(X509_STORE_CTX_get_current_cert(ctx)); return rb_funcall(mSSL, rb_intern("verify_certificate_identity"), 2, cert_obj, hostname); } static int ossl_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { VALUE cb, ssl_obj, sslctx_obj, verify_hostname, ret; SSL *ssl; int status; ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); cb = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_vcb_idx); ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); sslctx_obj = rb_attr_get(ssl_obj, id_i_context); verify_hostname = rb_attr_get(sslctx_obj, id_i_verify_hostname); if (preverify_ok && RTEST(verify_hostname) && !SSL_is_server(ssl) && !X509_STORE_CTX_get_error_depth(ctx)) { ret = rb_protect(call_verify_certificate_identity, (VALUE)ctx, &status); if (status) { rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status)); return 0; } preverify_ok = ret == Qtrue; } return ossl_verify_cb_call(cb, preverify_ok, ctx); } static VALUE ossl_call_session_get_cb(VALUE ary) { VALUE ssl_obj, cb; Check_Type(ary, T_ARRAY); ssl_obj = rb_ary_entry(ary, 0); cb = rb_funcall(ssl_obj, rb_intern("session_get_cb"), 0); if (NIL_P(cb)) return Qnil; return rb_funcall(cb, rb_intern("call"), 1, ary); } /* this method is currently only called for servers (in OpenSSL <= 0.9.8e) */ static SSL_SESSION * #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) ossl_sslctx_session_get_cb(SSL *ssl, const unsigned char *buf, int len, int *copy) #else ossl_sslctx_session_get_cb(SSL *ssl, unsigned char *buf, int len, int *copy) #endif { VALUE ary, ssl_obj, ret_obj; SSL_SESSION *sess; void *ptr; int state = 0; OSSL_Debug("SSL SESSION get callback entered"); if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL) return NULL; ssl_obj = (VALUE)ptr; ary = rb_ary_new2(2); rb_ary_push(ary, ssl_obj); rb_ary_push(ary, rb_str_new((const char *)buf, len)); ret_obj = rb_protect(ossl_call_session_get_cb, ary, &state); if (state) { rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state)); return NULL; } if (!rb_obj_is_instance_of(ret_obj, cSSLSession)) return NULL; SafeGetSSLSession(ret_obj, sess); *copy = 1; return sess; } static VALUE ossl_call_session_new_cb(VALUE ary) { VALUE ssl_obj, cb; Check_Type(ary, T_ARRAY); ssl_obj = rb_ary_entry(ary, 0); cb = rb_funcall(ssl_obj, rb_intern("session_new_cb"), 0); if (NIL_P(cb)) return Qnil; return rb_funcall(cb, rb_intern("call"), 1, ary); } /* return 1 normal. return 0 removes the session */ static int ossl_sslctx_session_new_cb(SSL *ssl, SSL_SESSION *sess) { VALUE ary, ssl_obj, sess_obj; void *ptr; int state = 0; OSSL_Debug("SSL SESSION new callback entered"); if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL) return 1; ssl_obj = (VALUE)ptr; sess_obj = rb_obj_alloc(cSSLSession); SSL_SESSION_up_ref(sess); DATA_PTR(sess_obj) = sess; ary = rb_ary_new2(2); rb_ary_push(ary, ssl_obj); rb_ary_push(ary, sess_obj); rb_protect(ossl_call_session_new_cb, ary, &state); if (state) { rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state)); } /* * return 0 which means to OpenSSL that the session is still * valid (since we created Ruby Session object) and was not freed by us * with SSL_SESSION_free(). Call SSLContext#remove_session(sess) in * session_get_cb block if you don't want OpenSSL to cache the session * internally. */ return 0; } static VALUE ossl_call_session_remove_cb(VALUE ary) { VALUE sslctx_obj, cb; Check_Type(ary, T_ARRAY); sslctx_obj = rb_ary_entry(ary, 0); cb = rb_attr_get(sslctx_obj, id_i_session_remove_cb); if (NIL_P(cb)) return Qnil; return rb_funcall(cb, rb_intern("call"), 1, ary); } static void ossl_sslctx_session_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess) { VALUE ary, sslctx_obj, sess_obj; void *ptr; int state = 0; /* * This callback is also called for all sessions in the internal store * when SSL_CTX_free() is called. */ if (rb_during_gc()) return; OSSL_Debug("SSL SESSION remove callback entered"); if ((ptr = SSL_CTX_get_ex_data(ctx, ossl_ssl_ex_ptr_idx)) == NULL) return; sslctx_obj = (VALUE)ptr; sess_obj = rb_obj_alloc(cSSLSession); SSL_SESSION_up_ref(sess); DATA_PTR(sess_obj) = sess; ary = rb_ary_new2(2); rb_ary_push(ary, sslctx_obj); rb_ary_push(ary, sess_obj); rb_protect(ossl_call_session_remove_cb, ary, &state); if (state) { /* the SSL_CTX is frozen, nowhere to save state. there is no common accessor method to check it either. rb_ivar_set(sslctx_obj, ID_callback_state, INT2NUM(state)); */ } } static VALUE ossl_sslctx_add_extra_chain_cert_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, arg)) { X509 *x509; SSL_CTX *ctx; GetSSLCTX(arg, ctx); x509 = DupX509CertPtr(i); if(!SSL_CTX_add_extra_chain_cert(ctx, x509)){ ossl_raise(eSSLError, NULL); } return i; } static VALUE ossl_sslctx_setup(VALUE self); #ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME static VALUE ossl_call_servername_cb(VALUE ary) { VALUE ssl_obj, sslctx_obj, cb, ret_obj; Check_Type(ary, T_ARRAY); ssl_obj = rb_ary_entry(ary, 0); sslctx_obj = rb_attr_get(ssl_obj, id_i_context); cb = rb_attr_get(sslctx_obj, id_i_servername_cb); if (NIL_P(cb)) return Qnil; ret_obj = rb_funcall(cb, rb_intern("call"), 1, ary); if (rb_obj_is_kind_of(ret_obj, cSSLContext)) { SSL *ssl; SSL_CTX *ctx2; ossl_sslctx_setup(ret_obj); GetSSL(ssl_obj, ssl); GetSSLCTX(ret_obj, ctx2); SSL_set_SSL_CTX(ssl, ctx2); rb_ivar_set(ssl_obj, id_i_context, ret_obj); } else if (!NIL_P(ret_obj)) { ossl_raise(rb_eArgError, "servername_cb must return an " "OpenSSL::SSL::SSLContext object or nil"); } return ret_obj; } static int ssl_servername_cb(SSL *ssl, int *ad, void *arg) { VALUE ary, ssl_obj; void *ptr; int state = 0; const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (!servername) return SSL_TLSEXT_ERR_OK; if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL) return SSL_TLSEXT_ERR_ALERT_FATAL; ssl_obj = (VALUE)ptr; ary = rb_ary_new2(2); rb_ary_push(ary, ssl_obj); rb_ary_push(ary, rb_str_new2(servername)); rb_protect(ossl_call_servername_cb, ary, &state); if (state) { rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state)); return SSL_TLSEXT_ERR_ALERT_FATAL; } return SSL_TLSEXT_ERR_OK; } #endif static void ssl_renegotiation_cb(const SSL *ssl) { VALUE ssl_obj, sslctx_obj, cb; void *ptr; if ((ptr = SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx)) == NULL) ossl_raise(eSSLError, "SSL object could not be retrieved"); ssl_obj = (VALUE)ptr; sslctx_obj = rb_attr_get(ssl_obj, id_i_context); cb = rb_attr_get(sslctx_obj, id_i_renegotiation_cb); if (NIL_P(cb)) return; (void) rb_funcall(cb, rb_intern("call"), 1, ssl_obj); } #if defined(HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB) || \ defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB) static VALUE ssl_npn_encode_protocol_i(VALUE cur, VALUE encoded) { int len = RSTRING_LENINT(cur); char len_byte; if (len < 1 || len > 255) ossl_raise(eSSLError, "Advertised protocol must have length 1..255"); /* Encode the length byte */ len_byte = len; rb_str_buf_cat(encoded, &len_byte, 1); rb_str_buf_cat(encoded, RSTRING_PTR(cur), len); return Qnil; } static VALUE ssl_encode_npn_protocols(VALUE protocols) { VALUE encoded = rb_str_new(NULL, 0); rb_iterate(rb_each, protocols, ssl_npn_encode_protocol_i, encoded); return encoded; } struct npn_select_cb_common_args { VALUE cb; const unsigned char *in; unsigned inlen; }; static VALUE npn_select_cb_common_i(VALUE tmp) { struct npn_select_cb_common_args *args = (void *)tmp; const unsigned char *in = args->in, *in_end = in + args->inlen; unsigned char l; long len; VALUE selected, protocols = rb_ary_new(); /* assume OpenSSL verifies this format */ /* The format is len_1|proto_1|...|len_n|proto_n */ while (in < in_end) { l = *in++; rb_ary_push(protocols, rb_str_new((const char *)in, l)); in += l; } selected = rb_funcall(args->cb, rb_intern("call"), 1, protocols); StringValue(selected); len = RSTRING_LEN(selected); if (len < 1 || len >= 256) { ossl_raise(eSSLError, "Selected protocol name must have length 1..255"); } return selected; } static int ssl_npn_select_cb_common(SSL *ssl, VALUE cb, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen) { VALUE selected; int status; struct npn_select_cb_common_args args; args.cb = cb; args.in = in; args.inlen = inlen; selected = rb_protect(npn_select_cb_common_i, (VALUE)&args, &status); if (status) { VALUE ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status)); return SSL_TLSEXT_ERR_ALERT_FATAL; } *out = (unsigned char *)RSTRING_PTR(selected); *outlen = (unsigned char)RSTRING_LEN(selected); return SSL_TLSEXT_ERR_OK; } #endif #ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB static int ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg) { VALUE protocols = (VALUE)arg; *out = (const unsigned char *) RSTRING_PTR(protocols); *outlen = RSTRING_LENINT(protocols); return SSL_TLSEXT_ERR_OK; } static int ssl_npn_select_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { VALUE sslctx_obj, cb; sslctx_obj = (VALUE) arg; cb = rb_attr_get(sslctx_obj, id_i_npn_select_cb); return ssl_npn_select_cb_common(ssl, cb, (const unsigned char **)out, outlen, in, inlen); } #endif #ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB static int ssl_alpn_select_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { VALUE sslctx_obj, cb; sslctx_obj = (VALUE) arg; cb = rb_attr_get(sslctx_obj, id_i_alpn_select_cb); return ssl_npn_select_cb_common(ssl, cb, out, outlen, in, inlen); } #endif /* This function may serve as the entry point to support further callbacks. */ static void ssl_info_cb(const SSL *ssl, int where, int val) { int is_server = SSL_is_server((SSL *)ssl); if (is_server && where & SSL_CB_HANDSHAKE_START) { ssl_renegotiation_cb(ssl); } } /* * Gets various OpenSSL options. */ static VALUE ossl_sslctx_get_options(VALUE self) { SSL_CTX *ctx; GetSSLCTX(self, ctx); return LONG2NUM(SSL_CTX_get_options(ctx)); } /* * Sets various OpenSSL options. */ static VALUE ossl_sslctx_set_options(VALUE self, VALUE options) { SSL_CTX *ctx; rb_check_frozen(self); GetSSLCTX(self, ctx); SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx)); if (NIL_P(options)) { SSL_CTX_set_options(ctx, SSL_OP_ALL); } else { SSL_CTX_set_options(ctx, NUM2LONG(options)); } return self; } /* * call-seq: * ctx.setup => Qtrue # first time * ctx.setup => nil # thereafter * * This method is called automatically when a new SSLSocket is created. * However, it is not thread-safe and must be called before creating * SSLSocket objects in a multi-threaded program. */ static VALUE ossl_sslctx_setup(VALUE self) { SSL_CTX *ctx; X509 *cert = NULL, *client_ca = NULL; EVP_PKEY *key = NULL; char *ca_path = NULL, *ca_file = NULL; int verify_mode; long i; VALUE val; if(OBJ_FROZEN(self)) return Qnil; GetSSLCTX(self, ctx); #if !defined(OPENSSL_NO_DH) SSL_CTX_set_tmp_dh_callback(ctx, ossl_tmp_dh_callback); #endif #if !defined(OPENSSL_NO_EC) /* We added SSLContext#tmp_ecdh_callback= in Ruby 2.3.0, * but SSL_CTX_set_tmp_ecdh_callback() was removed in OpenSSL 1.1.0. */ if (RTEST(rb_attr_get(self, id_i_tmp_ecdh_callback))) { # if defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) rb_warn("#tmp_ecdh_callback= is deprecated; use #ecdh_curves= instead"); SSL_CTX_set_tmp_ecdh_callback(ctx, ossl_tmp_ecdh_callback); # if defined(HAVE_SSL_CTX_SET_ECDH_AUTO) /* tmp_ecdh_callback and ecdh_auto conflict; OpenSSL ignores * tmp_ecdh_callback. So disable ecdh_auto. */ if (!SSL_CTX_set_ecdh_auto(ctx, 0)) ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto"); # endif # else ossl_raise(eSSLError, "OpenSSL does not support tmp_ecdh_callback; " "use #ecdh_curves= instead"); # endif } #endif /* OPENSSL_NO_EC */ val = rb_attr_get(self, id_i_cert_store); if (!NIL_P(val)) { X509_STORE *store = GetX509StorePtr(val); /* NO NEED TO DUP */ SSL_CTX_set_cert_store(ctx, store); #if !defined(HAVE_X509_STORE_UP_REF) /* * WORKAROUND: * X509_STORE can count references, but * X509_STORE_free() doesn't care it. * So we won't increment it but mark it by ex_data. */ SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_store_p, (void *)1); #else /* Fixed in OpenSSL 1.0.2; bff9ce4db38b (master), 5b4b9ce976fc (1.0.2) */ X509_STORE_up_ref(store); #endif } val = rb_attr_get(self, id_i_extra_chain_cert); if(!NIL_P(val)){ rb_block_call(val, rb_intern("each"), 0, 0, ossl_sslctx_add_extra_chain_cert_i, self); } /* private key may be bundled in certificate file. */ val = rb_attr_get(self, id_i_cert); cert = NIL_P(val) ? NULL : GetX509CertPtr(val); /* NO DUP NEEDED */ val = rb_attr_get(self, id_i_key); key = NIL_P(val) ? NULL : GetPrivPKeyPtr(val); /* NO DUP NEEDED */ if (cert && key) { if (!SSL_CTX_use_certificate(ctx, cert)) { /* Adds a ref => Safe to FREE */ ossl_raise(eSSLError, "SSL_CTX_use_certificate"); } if (!SSL_CTX_use_PrivateKey(ctx, key)) { /* Adds a ref => Safe to FREE */ ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey"); } if (!SSL_CTX_check_private_key(ctx)) { ossl_raise(eSSLError, "SSL_CTX_check_private_key"); } } val = rb_attr_get(self, id_i_client_ca); if(!NIL_P(val)){ if (RB_TYPE_P(val, T_ARRAY)) { for(i = 0; i < RARRAY_LEN(val); i++){ client_ca = GetX509CertPtr(RARRAY_AREF(val, i)); if (!SSL_CTX_add_client_CA(ctx, client_ca)){ /* Copies X509_NAME => FREE it. */ ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); } } } else{ client_ca = GetX509CertPtr(val); /* NO DUP NEEDED. */ if (!SSL_CTX_add_client_CA(ctx, client_ca)){ /* Copies X509_NAME => FREE it. */ ossl_raise(eSSLError, "SSL_CTX_add_client_CA"); } } } val = rb_attr_get(self, id_i_ca_file); ca_file = NIL_P(val) ? NULL : StringValueCStr(val); val = rb_attr_get(self, id_i_ca_path); ca_path = NIL_P(val) ? NULL : StringValueCStr(val); if(ca_file || ca_path){ if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_path)) rb_warning("can't set verify locations"); } val = rb_attr_get(self, id_i_verify_mode); verify_mode = NIL_P(val) ? SSL_VERIFY_NONE : NUM2INT(val); SSL_CTX_set_verify(ctx, verify_mode, ossl_ssl_verify_callback); if (RTEST(rb_attr_get(self, id_i_client_cert_cb))) SSL_CTX_set_client_cert_cb(ctx, ossl_client_cert_cb); val = rb_attr_get(self, id_i_timeout); if(!NIL_P(val)) SSL_CTX_set_timeout(ctx, NUM2LONG(val)); val = rb_attr_get(self, id_i_verify_depth); if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2INT(val)); #ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB val = rb_attr_get(self, id_i_npn_protocols); if (!NIL_P(val)) { VALUE encoded = ssl_encode_npn_protocols(val); rb_ivar_set(self, id_npn_protocols_encoded, encoded); SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)encoded); OSSL_Debug("SSL NPN advertise callback added"); } if (RTEST(rb_attr_get(self, id_i_npn_select_cb))) { SSL_CTX_set_next_proto_select_cb(ctx, ssl_npn_select_cb, (void *) self); OSSL_Debug("SSL NPN select callback added"); } #endif #ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB val = rb_attr_get(self, id_i_alpn_protocols); if (!NIL_P(val)) { VALUE rprotos = ssl_encode_npn_protocols(val); /* returns 0 on success */ if (SSL_CTX_set_alpn_protos(ctx, (unsigned char *)RSTRING_PTR(rprotos), RSTRING_LENINT(rprotos))) ossl_raise(eSSLError, "SSL_CTX_set_alpn_protos"); OSSL_Debug("SSL ALPN values added"); } if (RTEST(rb_attr_get(self, id_i_alpn_select_cb))) { SSL_CTX_set_alpn_select_cb(ctx, ssl_alpn_select_cb, (void *) self); OSSL_Debug("SSL ALPN select callback added"); } #endif rb_obj_freeze(self); val = rb_attr_get(self, id_i_session_id_context); if (!NIL_P(val)){ StringValue(val); if (!SSL_CTX_set_session_id_context(ctx, (unsigned char *)RSTRING_PTR(val), RSTRING_LENINT(val))){ ossl_raise(eSSLError, "SSL_CTX_set_session_id_context"); } } if (RTEST(rb_attr_get(self, id_i_session_get_cb))) { SSL_CTX_sess_set_get_cb(ctx, ossl_sslctx_session_get_cb); OSSL_Debug("SSL SESSION get callback added"); } if (RTEST(rb_attr_get(self, id_i_session_new_cb))) { SSL_CTX_sess_set_new_cb(ctx, ossl_sslctx_session_new_cb); OSSL_Debug("SSL SESSION new callback added"); } if (RTEST(rb_attr_get(self, id_i_session_remove_cb))) { SSL_CTX_sess_set_remove_cb(ctx, ossl_sslctx_session_remove_cb); OSSL_Debug("SSL SESSION remove callback added"); } #ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME val = rb_attr_get(self, id_i_servername_cb); if (!NIL_P(val)) { SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb); OSSL_Debug("SSL TLSEXT servername callback added"); } #endif return Qtrue; } static VALUE ossl_ssl_cipher_to_ary(const SSL_CIPHER *cipher) { VALUE ary; int bits, alg_bits; ary = rb_ary_new2(4); rb_ary_push(ary, rb_str_new2(SSL_CIPHER_get_name(cipher))); rb_ary_push(ary, rb_str_new2(SSL_CIPHER_get_version(cipher))); bits = SSL_CIPHER_get_bits(cipher, &alg_bits); rb_ary_push(ary, INT2NUM(bits)); rb_ary_push(ary, INT2NUM(alg_bits)); return ary; } /* * call-seq: * ctx.ciphers => [[name, version, bits, alg_bits], ...] * * The list of cipher suites configured for this context. */ static VALUE ossl_sslctx_get_ciphers(VALUE self) { SSL_CTX *ctx; STACK_OF(SSL_CIPHER) *ciphers; const SSL_CIPHER *cipher; VALUE ary; int i, num; GetSSLCTX(self, ctx); ciphers = SSL_CTX_get_ciphers(ctx); if (!ciphers) return rb_ary_new(); num = sk_SSL_CIPHER_num(ciphers); ary = rb_ary_new2(num); for(i = 0; i < num; i++){ cipher = sk_SSL_CIPHER_value(ciphers, i); rb_ary_push(ary, ossl_ssl_cipher_to_ary(cipher)); } return ary; } /* * call-seq: * ctx.ciphers = "cipher1:cipher2:..." * ctx.ciphers = [name, ...] * ctx.ciphers = [[name, version, bits, alg_bits], ...] * * Sets the list of available cipher suites for this context. Note in a server * context some ciphers require the appropriate certificates. For example, an * RSA cipher suite can only be chosen when an RSA certificate is available. */ static VALUE ossl_sslctx_set_ciphers(VALUE self, VALUE v) { SSL_CTX *ctx; VALUE str, elem; int i; rb_check_frozen(self); if (NIL_P(v)) return v; else if (RB_TYPE_P(v, T_ARRAY)) { str = rb_str_new(0, 0); for (i = 0; i < RARRAY_LEN(v); i++) { elem = rb_ary_entry(v, i); if (RB_TYPE_P(elem, T_ARRAY)) elem = rb_ary_entry(elem, 0); elem = rb_String(elem); rb_str_append(str, elem); if (i < RARRAY_LEN(v)-1) rb_str_cat2(str, ":"); } } else { str = v; StringValue(str); } GetSSLCTX(self, ctx); if (!SSL_CTX_set_cipher_list(ctx, StringValueCStr(str))) { ossl_raise(eSSLError, "SSL_CTX_set_cipher_list"); } return v; } #if !defined(OPENSSL_NO_EC) /* * call-seq: * ctx.ecdh_curves = curve_list -> curve_list * * Sets the list of "supported elliptic curves" for this context. * * For a TLS client, the list is directly used in the Supported Elliptic Curves * Extension. For a server, the list is used by OpenSSL to determine the set of * shared curves. OpenSSL will pick the most appropriate one from it. * * Note that this works differently with old OpenSSL (<= 1.0.1). Only one curve * can be set, and this has no effect for TLS clients. * * === Example * ctx1 = OpenSSL::SSL::SSLContext.new * ctx1.ecdh_curves = "X25519:P-256:P-224" * svr = OpenSSL::SSL::SSLServer.new(tcp_svr, ctx1) * Thread.new { svr.accept } * * ctx2 = OpenSSL::SSL::SSLContext.new * ctx2.ecdh_curves = "P-256" * cli = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx2) * cli.connect * * p cli.tmp_key.group.curve_name * # => "prime256v1" (is an alias for NIST P-256) */ static VALUE ossl_sslctx_set_ecdh_curves(VALUE self, VALUE arg) { SSL_CTX *ctx; rb_check_frozen(self); GetSSLCTX(self, ctx); StringValueCStr(arg); #if defined(HAVE_SSL_CTX_SET1_CURVES_LIST) if (!SSL_CTX_set1_curves_list(ctx, RSTRING_PTR(arg))) ossl_raise(eSSLError, NULL); #else /* OpenSSL does not have SSL_CTX_set1_curves_list()... Fallback to * SSL_CTX_set_tmp_ecdh(). So only the first curve is used. */ { VALUE curve, splitted; EC_KEY *ec; int nid; splitted = rb_str_split(arg, ":"); if (!RARRAY_LEN(splitted)) ossl_raise(eSSLError, "invalid input format"); curve = RARRAY_AREF(splitted, 0); StringValueCStr(curve); /* SSL_CTX_set1_curves_list() accepts NIST names */ nid = EC_curve_nist2nid(RSTRING_PTR(curve)); if (nid == NID_undef) nid = OBJ_txt2nid(RSTRING_PTR(curve)); if (nid == NID_undef) ossl_raise(eSSLError, "unknown curve name"); ec = EC_KEY_new_by_curve_name(nid); if (!ec) ossl_raise(eSSLError, NULL); EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE); if (!SSL_CTX_set_tmp_ecdh(ctx, ec)) { EC_KEY_free(ec); ossl_raise(eSSLError, "SSL_CTX_set_tmp_ecdh"); } EC_KEY_free(ec); # if defined(HAVE_SSL_CTX_SET_ECDH_AUTO) /* tmp_ecdh and ecdh_auto conflict. tmp_ecdh is ignored when ecdh_auto * is enabled. So disable ecdh_auto. */ if (!SSL_CTX_set_ecdh_auto(ctx, 0)) ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto"); # endif } #endif return arg; } #else #define ossl_sslctx_set_ecdh_curves rb_f_notimplement #endif /* * call-seq: * ctx.security_level -> Integer * * Returns the security level for the context. * * See also OpenSSL::SSL::SSLContext#security_level=. */ static VALUE ossl_sslctx_get_security_level(VALUE self) { SSL_CTX *ctx; GetSSLCTX(self, ctx); #if defined(HAVE_SSL_CTX_GET_SECURITY_LEVEL) return INT2NUM(SSL_CTX_get_security_level(ctx)); #else (void)ctx; return INT2FIX(0); #endif } /* * call-seq: * ctx.security_level = integer * * Sets the security level for the context. OpenSSL limits parameters according * to the level. The "parameters" include: ciphersuites, curves, key sizes, * certificate signature algorithms, protocol version and so on. For example, * level 1 rejects parameters offering below 80 bits of security, such as * ciphersuites using MD5 for the MAC or RSA keys shorter than 1024 bits. * * Note that attempts to set such parameters with insufficient security are * also blocked. You need to lower the level first. * * This feature is not supported in OpenSSL < 1.1.0, and setting the level to * other than 0 will raise NotImplementedError. Level 0 means everything is * permitted, the same behavior as previous versions of OpenSSL. * * See the manpage of SSL_CTX_set_security_level(3) for details. */ static VALUE ossl_sslctx_set_security_level(VALUE self, VALUE value) { SSL_CTX *ctx; rb_check_frozen(self); GetSSLCTX(self, ctx); #if defined(HAVE_SSL_CTX_GET_SECURITY_LEVEL) SSL_CTX_set_security_level(ctx, NUM2INT(value)); #else (void)ctx; if (NUM2INT(value) != 0) ossl_raise(rb_eNotImpError, "setting security level to other than 0 is " "not supported in this version of OpenSSL"); #endif return value; } /* * call-seq: * ctx.session_add(session) -> true | false * * Adds +session+ to the session cache. */ static VALUE ossl_sslctx_session_add(VALUE self, VALUE arg) { SSL_CTX *ctx; SSL_SESSION *sess; GetSSLCTX(self, ctx); SafeGetSSLSession(arg, sess); return SSL_CTX_add_session(ctx, sess) == 1 ? Qtrue : Qfalse; } /* * call-seq: * ctx.session_remove(session) -> true | false * * Removes +session+ from the session cache. */ static VALUE ossl_sslctx_session_remove(VALUE self, VALUE arg) { SSL_CTX *ctx; SSL_SESSION *sess; GetSSLCTX(self, ctx); SafeGetSSLSession(arg, sess); return SSL_CTX_remove_session(ctx, sess) == 1 ? Qtrue : Qfalse; } /* * call-seq: * ctx.session_cache_mode -> Integer * * The current session cache mode. */ static VALUE ossl_sslctx_get_session_cache_mode(VALUE self) { SSL_CTX *ctx; GetSSLCTX(self, ctx); return LONG2NUM(SSL_CTX_get_session_cache_mode(ctx)); } /* * call-seq: * ctx.session_cache_mode=(integer) -> Integer * * Sets the SSL session cache mode. Bitwise-or together the desired * SESSION_CACHE_* constants to set. See SSL_CTX_set_session_cache_mode(3) for * details. */ static VALUE ossl_sslctx_set_session_cache_mode(VALUE self, VALUE arg) { SSL_CTX *ctx; GetSSLCTX(self, ctx); SSL_CTX_set_session_cache_mode(ctx, NUM2LONG(arg)); return arg; } /* * call-seq: * ctx.session_cache_size -> Integer * * Returns the current session cache size. Zero is used to represent an * unlimited cache size. */ static VALUE ossl_sslctx_get_session_cache_size(VALUE self) { SSL_CTX *ctx; GetSSLCTX(self, ctx); return LONG2NUM(SSL_CTX_sess_get_cache_size(ctx)); } /* * call-seq: * ctx.session_cache_size=(integer) -> Integer * * Sets the session cache size. Returns the previously valid session cache * size. Zero is used to represent an unlimited session cache size. */ static VALUE ossl_sslctx_set_session_cache_size(VALUE self, VALUE arg) { SSL_CTX *ctx; GetSSLCTX(self, ctx); SSL_CTX_sess_set_cache_size(ctx, NUM2LONG(arg)); return arg; } /* * call-seq: * ctx.session_cache_stats -> Hash * * Returns a Hash containing the following keys: * * :accept:: Number of started SSL/TLS handshakes in server mode * :accept_good:: Number of established SSL/TLS sessions in server mode * :accept_renegotiate:: Number of start renegotiations in server mode * :cache_full:: Number of sessions that were removed due to cache overflow * :cache_hits:: Number of successfully reused connections * :cache_misses:: Number of sessions proposed by clients that were not found * in the cache * :cache_num:: Number of sessions in the internal session cache * :cb_hits:: Number of sessions retrieved from the external cache in server * mode * :connect:: Number of started SSL/TLS handshakes in client mode * :connect_good:: Number of established SSL/TLS sessions in client mode * :connect_renegotiate:: Number of start renegotiations in client mode * :timeouts:: Number of sessions proposed by clients that were found in the * cache but had expired due to timeouts */ static VALUE ossl_sslctx_get_session_cache_stats(VALUE self) { SSL_CTX *ctx; VALUE hash; GetSSLCTX(self, ctx); hash = rb_hash_new(); rb_hash_aset(hash, ID2SYM(rb_intern("cache_num")), LONG2NUM(SSL_CTX_sess_number(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("connect")), LONG2NUM(SSL_CTX_sess_connect(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("connect_good")), LONG2NUM(SSL_CTX_sess_connect_good(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("connect_renegotiate")), LONG2NUM(SSL_CTX_sess_connect_renegotiate(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("accept")), LONG2NUM(SSL_CTX_sess_accept(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("accept_good")), LONG2NUM(SSL_CTX_sess_accept_good(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("accept_renegotiate")), LONG2NUM(SSL_CTX_sess_accept_renegotiate(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("cache_hits")), LONG2NUM(SSL_CTX_sess_hits(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("cb_hits")), LONG2NUM(SSL_CTX_sess_cb_hits(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("cache_misses")), LONG2NUM(SSL_CTX_sess_misses(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("cache_full")), LONG2NUM(SSL_CTX_sess_cache_full(ctx))); rb_hash_aset(hash, ID2SYM(rb_intern("timeouts")), LONG2NUM(SSL_CTX_sess_timeouts(ctx))); return hash; } /* * call-seq: * ctx.flush_sessions(time | nil) -> self * * Removes sessions in the internal cache that have expired at +time+. */ static VALUE ossl_sslctx_flush_sessions(int argc, VALUE *argv, VALUE self) { VALUE arg1; SSL_CTX *ctx; time_t tm = 0; rb_scan_args(argc, argv, "01", &arg1); GetSSLCTX(self, ctx); if (NIL_P(arg1)) { tm = time(0); } else if (rb_obj_is_instance_of(arg1, rb_cTime)) { tm = NUM2LONG(rb_funcall(arg1, rb_intern("to_i"), 0)); } else { ossl_raise(rb_eArgError, "arg must be Time or nil"); } SSL_CTX_flush_sessions(ctx, (long)tm); return self; } /* * SSLSocket class */ #ifndef OPENSSL_NO_SOCK static inline int ssl_started(SSL *ssl) { /* the FD is set in ossl_ssl_setup(), called by #connect or #accept */ return SSL_get_fd(ssl) >= 0; } static void ossl_ssl_free(void *ssl) { SSL_free(ssl); } const rb_data_type_t ossl_ssl_type = { "OpenSSL/SSL", { 0, ossl_ssl_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static VALUE ossl_ssl_s_alloc(VALUE klass) { return TypedData_Wrap_Struct(klass, &ossl_ssl_type, NULL); } /* * call-seq: * SSLSocket.new(io) => aSSLSocket * SSLSocket.new(io, ctx) => aSSLSocket * * Creates a new SSL socket from +io+ which must be a real IO object (not an * IO-like object that responds to read/write). * * If +ctx+ is provided the SSL Sockets initial params will be taken from * the context. * * The OpenSSL::Buffering module provides additional IO methods. * * This method will freeze the SSLContext if one is provided; * however, session management is still allowed in the frozen SSLContext. */ static VALUE ossl_ssl_initialize(int argc, VALUE *argv, VALUE self) { VALUE io, v_ctx, verify_cb; SSL *ssl; SSL_CTX *ctx; TypedData_Get_Struct(self, SSL, &ossl_ssl_type, ssl); if (ssl) ossl_raise(eSSLError, "SSL already initialized"); if (rb_scan_args(argc, argv, "11", &io, &v_ctx) == 1) v_ctx = rb_funcall(cSSLContext, rb_intern("new"), 0); GetSSLCTX(v_ctx, ctx); rb_ivar_set(self, id_i_context, v_ctx); ossl_sslctx_setup(v_ctx); if (rb_respond_to(io, rb_intern("nonblock="))) rb_funcall(io, rb_intern("nonblock="), 1, Qtrue); rb_ivar_set(self, id_i_io, io); ssl = SSL_new(ctx); if (!ssl) ossl_raise(eSSLError, NULL); RTYPEDDATA_DATA(self) = ssl; SSL_set_ex_data(ssl, ossl_ssl_ex_ptr_idx, (void *)self); SSL_set_info_callback(ssl, ssl_info_cb); verify_cb = rb_attr_get(v_ctx, id_i_verify_callback); SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)verify_cb); rb_call_super(0, NULL); return self; } static VALUE ossl_ssl_setup(VALUE self) { VALUE io; SSL *ssl; rb_io_t *fptr; GetSSL(self, ssl); if (ssl_started(ssl)) return Qtrue; io = rb_attr_get(self, id_i_io); GetOpenFile(io, fptr); rb_io_check_readable(fptr); rb_io_check_writable(fptr); if (!SSL_set_fd(ssl, TO_SOCKET(FPTR_TO_FD(fptr)))) ossl_raise(eSSLError, "SSL_set_fd"); return Qtrue; } #ifdef _WIN32 #define ssl_get_error(ssl, ret) (errno = rb_w32_map_errno(WSAGetLastError()), SSL_get_error((ssl), (ret))) #else #define ssl_get_error(ssl, ret) SSL_get_error((ssl), (ret)) #endif static void write_would_block(int nonblock) { if (nonblock) ossl_raise(eSSLErrorWaitWritable, "write would block"); } static void read_would_block(int nonblock) { if (nonblock) ossl_raise(eSSLErrorWaitReadable, "read would block"); } static int no_exception_p(VALUE opts) { if (RB_TYPE_P(opts, T_HASH) && rb_hash_lookup2(opts, sym_exception, Qundef) == Qfalse) return 1; return 0; } static VALUE ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts) { SSL *ssl; rb_io_t *fptr; int ret, ret2; VALUE cb_state; int nonblock = opts != Qfalse; rb_ivar_set(self, ID_callback_state, Qnil); GetSSL(self, ssl); GetOpenFile(rb_attr_get(self, id_i_io), fptr); for(;;){ ret = func(ssl); cb_state = rb_attr_get(self, ID_callback_state); if (!NIL_P(cb_state)) { /* must cleanup OpenSSL error stack before re-raising */ ossl_clear_error(); rb_jump_tag(NUM2INT(cb_state)); } if (ret > 0) break; switch((ret2 = ssl_get_error(ssl, ret))){ case SSL_ERROR_WANT_WRITE: if (no_exception_p(opts)) { return sym_wait_writable; } write_would_block(nonblock); rb_io_wait_writable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_WANT_READ: if (no_exception_p(opts)) { return sym_wait_readable; } read_would_block(nonblock); rb_io_wait_readable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_SYSCALL: if (errno) rb_sys_fail(funcname); ossl_raise(eSSLError, "%s SYSCALL returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl)); default: ossl_raise(eSSLError, "%s returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl)); } } return self; } /* * call-seq: * ssl.connect => self * * Initiates an SSL/TLS handshake with a server. The handshake may be started * after unencrypted data has been sent over the socket. */ static VALUE ossl_ssl_connect(VALUE self) { ossl_ssl_setup(self); return ossl_start_ssl(self, SSL_connect, "SSL_connect", Qfalse); } /* * call-seq: * ssl.connect_nonblock([options]) => self * * Initiates the SSL/TLS handshake as a client in non-blocking manner. * * # emulates blocking connect * begin * ssl.connect_nonblock * rescue IO::WaitReadable * IO.select([s2]) * retry * rescue IO::WaitWritable * IO.select(nil, [s2]) * retry * end * * By specifying `exception: false`, the options hash allows you to indicate * that connect_nonblock should not raise an IO::WaitReadable or * IO::WaitWritable exception, but return the symbol :wait_readable or * :wait_writable instead. */ static VALUE ossl_ssl_connect_nonblock(int argc, VALUE *argv, VALUE self) { VALUE opts; rb_scan_args(argc, argv, "0:", &opts); ossl_ssl_setup(self); return ossl_start_ssl(self, SSL_connect, "SSL_connect", opts); } /* * call-seq: * ssl.accept => self * * Waits for a SSL/TLS client to initiate a handshake. The handshake may be * started after unencrypted data has been sent over the socket. */ static VALUE ossl_ssl_accept(VALUE self) { ossl_ssl_setup(self); return ossl_start_ssl(self, SSL_accept, "SSL_accept", Qfalse); } /* * call-seq: * ssl.accept_nonblock([options]) => self * * Initiates the SSL/TLS handshake as a server in non-blocking manner. * * # emulates blocking accept * begin * ssl.accept_nonblock * rescue IO::WaitReadable * IO.select([s2]) * retry * rescue IO::WaitWritable * IO.select(nil, [s2]) * retry * end * * By specifying `exception: false`, the options hash allows you to indicate * that accept_nonblock should not raise an IO::WaitReadable or * IO::WaitWritable exception, but return the symbol :wait_readable or * :wait_writable instead. */ static VALUE ossl_ssl_accept_nonblock(int argc, VALUE *argv, VALUE self) { VALUE opts; rb_scan_args(argc, argv, "0:", &opts); ossl_ssl_setup(self); return ossl_start_ssl(self, SSL_accept, "SSL_accept", opts); } static VALUE ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) { SSL *ssl; int ilen, nread = 0; VALUE len, str; rb_io_t *fptr; VALUE io, opts = Qnil; if (nonblock) { rb_scan_args(argc, argv, "11:", &len, &str, &opts); } else { rb_scan_args(argc, argv, "11", &len, &str); } ilen = NUM2INT(len); if (NIL_P(str)) str = rb_str_new(0, ilen); else { StringValue(str); if (RSTRING_LEN(str) >= ilen) rb_str_modify(str); else rb_str_modify_expand(str, ilen - RSTRING_LEN(str)); } OBJ_TAINT(str); rb_str_set_len(str, 0); if (ilen == 0) return str; GetSSL(self, ssl); io = rb_attr_get(self, id_i_io); GetOpenFile(io, fptr); if (ssl_started(ssl)) { for (;;){ nread = SSL_read(ssl, RSTRING_PTR(str), ilen); switch(ssl_get_error(ssl, nread)){ case SSL_ERROR_NONE: goto end; case SSL_ERROR_ZERO_RETURN: if (no_exception_p(opts)) { return Qnil; } rb_eof_error(); case SSL_ERROR_WANT_WRITE: if (no_exception_p(opts)) { return sym_wait_writable; } write_would_block(nonblock); rb_io_wait_writable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_WANT_READ: if (no_exception_p(opts)) { return sym_wait_readable; } read_would_block(nonblock); rb_io_wait_readable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_SYSCALL: if (!ERR_peek_error()) { if (errno) rb_sys_fail(0); else { /* * The underlying BIO returned 0. This is actually a * protocol error. But unfortunately, not all * implementations cleanly shutdown the TLS connection * but just shutdown/close the TCP connection. So report * EOF for now... */ if (no_exception_p(opts)) { return Qnil; } rb_eof_error(); } } default: ossl_raise(eSSLError, "SSL_read"); } } } else { ID meth = nonblock ? rb_intern("read_nonblock") : rb_intern("sysread"); rb_warning("SSL session is not started yet."); if (nonblock) return rb_funcall(io, meth, 3, len, str, opts); else return rb_funcall(io, meth, 2, len, str); } end: rb_str_set_len(str, nread); return str; } /* * call-seq: * ssl.sysread(length) => string * ssl.sysread(length, buffer) => buffer * * Reads +length+ bytes from the SSL connection. If a pre-allocated +buffer+ * is provided the data will be written into it. */ static VALUE ossl_ssl_read(int argc, VALUE *argv, VALUE self) { return ossl_ssl_read_internal(argc, argv, self, 0); } /* * call-seq: * ssl.sysread_nonblock(length) => string * ssl.sysread_nonblock(length, buffer) => buffer * ssl.sysread_nonblock(length[, buffer [, opts]) => buffer * * A non-blocking version of #sysread. Raises an SSLError if reading would * block. If "exception: false" is passed, this method returns a symbol of * :wait_readable, :wait_writable, or nil, rather than raising an exception. * * Reads +length+ bytes from the SSL connection. If a pre-allocated +buffer+ * is provided the data will be written into it. */ static VALUE ossl_ssl_read_nonblock(int argc, VALUE *argv, VALUE self) { return ossl_ssl_read_internal(argc, argv, self, 1); } static VALUE ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts) { SSL *ssl; int nwrite = 0; rb_io_t *fptr; int nonblock = opts != Qfalse; VALUE io; StringValue(str); GetSSL(self, ssl); io = rb_attr_get(self, id_i_io); GetOpenFile(io, fptr); if (ssl_started(ssl)) { for (;;){ int num = RSTRING_LENINT(str); /* SSL_write(3ssl) manpage states num == 0 is undefined */ if (num == 0) goto end; nwrite = SSL_write(ssl, RSTRING_PTR(str), num); switch(ssl_get_error(ssl, nwrite)){ case SSL_ERROR_NONE: goto end; case SSL_ERROR_WANT_WRITE: if (no_exception_p(opts)) { return sym_wait_writable; } write_would_block(nonblock); rb_io_wait_writable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_WANT_READ: if (no_exception_p(opts)) { return sym_wait_readable; } read_would_block(nonblock); rb_io_wait_readable(FPTR_TO_FD(fptr)); continue; case SSL_ERROR_SYSCALL: if (errno) rb_sys_fail(0); default: ossl_raise(eSSLError, "SSL_write"); } } } else { ID meth = nonblock ? rb_intern("write_nonblock") : rb_intern("syswrite"); rb_warning("SSL session is not started yet."); if (nonblock) return rb_funcall(io, meth, 2, str, opts); else return rb_funcall(io, meth, 1, str); } end: return INT2NUM(nwrite); } /* * call-seq: * ssl.syswrite(string) => Integer * * Writes +string+ to the SSL connection. */ static VALUE ossl_ssl_write(VALUE self, VALUE str) { return ossl_ssl_write_internal(self, str, Qfalse); } /* * call-seq: * ssl.syswrite_nonblock(string) => Integer * * Writes +string+ to the SSL connection in a non-blocking manner. Raises an * SSLError if writing would block. */ static VALUE ossl_ssl_write_nonblock(int argc, VALUE *argv, VALUE self) { VALUE str, opts; rb_scan_args(argc, argv, "1:", &str, &opts); return ossl_ssl_write_internal(self, str, opts); } /* * call-seq: * ssl.stop => nil * * Sends "close notify" to the peer and tries to shut down the SSL connection * gracefully. */ static VALUE ossl_ssl_stop(VALUE self) { SSL *ssl; int ret; GetSSL(self, ssl); if (!ssl_started(ssl)) return Qnil; ret = SSL_shutdown(ssl); if (ret == 1) /* Have already received close_notify */ return Qnil; if (ret == 0) /* Sent close_notify, but we don't wait for reply */ return Qnil; /* * XXX: Something happened. Possibly it failed because the underlying socket * is not writable/readable, since it is in non-blocking mode. We should do * some proper error handling using SSL_get_error() and maybe retry, but we * can't block here. Give up for now. */ ossl_clear_error(); return Qnil; } /* * call-seq: * ssl.cert => cert or nil * * The X509 certificate for this socket endpoint. */ static VALUE ossl_ssl_get_cert(VALUE self) { SSL *ssl; X509 *cert = NULL; GetSSL(self, ssl); /* * Is this OpenSSL bug? Should add a ref? * TODO: Ask for. */ cert = SSL_get_certificate(ssl); /* NO DUPs => DON'T FREE. */ if (!cert) { return Qnil; } return ossl_x509_new(cert); } /* * call-seq: * ssl.peer_cert => cert or nil * * The X509 certificate for this socket's peer. */ static VALUE ossl_ssl_get_peer_cert(VALUE self) { SSL *ssl; X509 *cert = NULL; VALUE obj; GetSSL(self, ssl); cert = SSL_get_peer_certificate(ssl); /* Adds a ref => Safe to FREE. */ if (!cert) { return Qnil; } obj = ossl_x509_new(cert); X509_free(cert); return obj; } /* * call-seq: * ssl.peer_cert_chain => [cert, ...] or nil * * The X509 certificate chain for this socket's peer. */ static VALUE ossl_ssl_get_peer_cert_chain(VALUE self) { SSL *ssl; STACK_OF(X509) *chain; X509 *cert; VALUE ary; int i, num; GetSSL(self, ssl); chain = SSL_get_peer_cert_chain(ssl); if(!chain) return Qnil; num = sk_X509_num(chain); ary = rb_ary_new2(num); for (i = 0; i < num; i++){ cert = sk_X509_value(chain, i); rb_ary_push(ary, ossl_x509_new(cert)); } return ary; } /* * call-seq: * ssl.ssl_version => String * * Returns a String representing the SSL/TLS version that was negotiated * for the connection, for example "TLSv1.2". */ static VALUE ossl_ssl_get_version(VALUE self) { SSL *ssl; GetSSL(self, ssl); return rb_str_new2(SSL_get_version(ssl)); } /* * call-seq: * ssl.cipher => [name, version, bits, alg_bits] * * The cipher being used for the current connection */ static VALUE ossl_ssl_get_cipher(VALUE self) { SSL *ssl; SSL_CIPHER *cipher; GetSSL(self, ssl); cipher = (SSL_CIPHER *)SSL_get_current_cipher(ssl); return ossl_ssl_cipher_to_ary(cipher); } /* * call-seq: * ssl.state => string * * A description of the current connection state. This is for diagnostic * purposes only. */ static VALUE ossl_ssl_get_state(VALUE self) { SSL *ssl; VALUE ret; GetSSL(self, ssl); ret = rb_str_new2(SSL_state_string(ssl)); if (ruby_verbose) { rb_str_cat2(ret, ": "); rb_str_cat2(ret, SSL_state_string_long(ssl)); } return ret; } /* * call-seq: * ssl.pending => Integer * * The number of bytes that are immediately available for reading. */ static VALUE ossl_ssl_pending(VALUE self) { SSL *ssl; GetSSL(self, ssl); return INT2NUM(SSL_pending(ssl)); } /* * call-seq: * ssl.session_reused? -> true | false * * Returns true if a reused session was negotiated during the handshake. */ static VALUE ossl_ssl_session_reused(VALUE self) { SSL *ssl; GetSSL(self, ssl); return SSL_session_reused(ssl) ? Qtrue : Qfalse; } /* * call-seq: * ssl.session = session -> session * * Sets the Session to be used when the connection is established. */ static VALUE ossl_ssl_set_session(VALUE self, VALUE arg1) { SSL *ssl; SSL_SESSION *sess; GetSSL(self, ssl); SafeGetSSLSession(arg1, sess); if (SSL_set_session(ssl, sess) != 1) ossl_raise(eSSLError, "SSL_set_session"); return arg1; } #ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME /* * call-seq: * ssl.hostname = hostname -> hostname * * Sets the server hostname used for SNI. This needs to be set before * SSLSocket#connect. */ static VALUE ossl_ssl_set_hostname(VALUE self, VALUE arg) { SSL *ssl; char *hostname = NULL; GetSSL(self, ssl); if (!NIL_P(arg)) hostname = StringValueCStr(arg); if (!SSL_set_tlsext_host_name(ssl, hostname)) ossl_raise(eSSLError, NULL); /* for SSLSocket#hostname */ rb_ivar_set(self, id_i_hostname, arg); return arg; } #endif /* * call-seq: * ssl.verify_result => Integer * * Returns the result of the peer certificates verification. See verify(1) * for error values and descriptions. * * If no peer certificate was presented X509_V_OK is returned. */ static VALUE ossl_ssl_get_verify_result(VALUE self) { SSL *ssl; GetSSL(self, ssl); return INT2NUM(SSL_get_verify_result(ssl)); } /* * call-seq: * ssl.client_ca => [x509name, ...] * * Returns the list of client CAs. Please note that in contrast to * SSLContext#client_ca= no array of X509::Certificate is returned but * X509::Name instances of the CA's subject distinguished name. * * In server mode, returns the list set by SSLContext#client_ca=. * In client mode, returns the list of client CAs sent from the server. */ static VALUE ossl_ssl_get_client_ca_list(VALUE self) { SSL *ssl; STACK_OF(X509_NAME) *ca; GetSSL(self, ssl); ca = SSL_get_client_CA_list(ssl); return ossl_x509name_sk2ary(ca); } # ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB /* * call-seq: * ssl.npn_protocol => String | nil * * Returns the protocol string that was finally selected by the client * during the handshake. */ static VALUE ossl_ssl_npn_protocol(VALUE self) { SSL *ssl; const unsigned char *out; unsigned int outlen; GetSSL(self, ssl); SSL_get0_next_proto_negotiated(ssl, &out, &outlen); if (!outlen) return Qnil; else return rb_str_new((const char *) out, outlen); } # endif # ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB /* * call-seq: * ssl.alpn_protocol => String | nil * * Returns the ALPN protocol string that was finally selected by the server * during the handshake. */ static VALUE ossl_ssl_alpn_protocol(VALUE self) { SSL *ssl; const unsigned char *out; unsigned int outlen; GetSSL(self, ssl); SSL_get0_alpn_selected(ssl, &out, &outlen); if (!outlen) return Qnil; else return rb_str_new((const char *) out, outlen); } # endif # ifdef HAVE_SSL_GET_SERVER_TMP_KEY /* * call-seq: * ssl.tmp_key => PKey or nil * * Returns the ephemeral key used in case of forward secrecy cipher. */ static VALUE ossl_ssl_tmp_key(VALUE self) { SSL *ssl; EVP_PKEY *key; GetSSL(self, ssl); if (!SSL_get_server_tmp_key(ssl, &key)) return Qnil; return ossl_pkey_new(key); } # endif /* defined(HAVE_SSL_GET_SERVER_TMP_KEY) */ #endif /* !defined(OPENSSL_NO_SOCK) */ #undef rb_intern #define rb_intern(s) rb_intern_const(s) void Init_ossl_ssl(void) { int i; VALUE ary; #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); rb_mWaitReadable = rb_define_module_under(rb_cIO, "WaitReadable"); rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable"); #endif ID_callback_state = rb_intern("callback_state"); ossl_ssl_ex_vcb_idx = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_vcb_idx",0,0,0); ossl_ssl_ex_store_p = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_store_p",0,0,0); ossl_ssl_ex_ptr_idx = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_ptr_idx",0,0,0); /* Document-module: OpenSSL::SSL * * Use SSLContext to set up the parameters for a TLS (former SSL) * connection. Both client and server TLS connections are supported, * SSLSocket and SSLServer may be used in conjunction with an instance * of SSLContext to set up connections. */ mSSL = rb_define_module_under(mOSSL, "SSL"); /* Document-module: OpenSSL::ExtConfig * * This module contains configuration information about the SSL extension, * for example if socket support is enabled, or the host name TLS extension * is enabled. Constants in this module will always be defined, but contain * `true` or `false` values depending on the configuration of your OpenSSL * installation. */ mSSLExtConfig = rb_define_module_under(mOSSL, "ExtConfig"); /* Document-class: OpenSSL::SSL::SSLError * * Generic error class raised by SSLSocket and SSLContext. */ eSSLError = rb_define_class_under(mSSL, "SSLError", eOSSLError); eSSLErrorWaitReadable = rb_define_class_under(mSSL, "SSLErrorWaitReadable", eSSLError); rb_include_module(eSSLErrorWaitReadable, rb_mWaitReadable); eSSLErrorWaitWritable = rb_define_class_under(mSSL, "SSLErrorWaitWritable", eSSLError); rb_include_module(eSSLErrorWaitWritable, rb_mWaitWritable); Init_ossl_ssl_session(); /* Document-class: OpenSSL::SSL::SSLContext * * An SSLContext is used to set various options regarding certificates, * algorithms, verification, session caching, etc. The SSLContext is * used to create an SSLSocket. * * All attributes must be set before creating an SSLSocket as the * SSLContext will be frozen afterward. */ cSSLContext = rb_define_class_under(mSSL, "SSLContext", rb_cObject); rb_define_alloc_func(cSSLContext, ossl_sslctx_s_alloc); rb_undef_method(cSSLContext, "initialize_copy"); /* * Context certificate */ rb_attr(cSSLContext, rb_intern("cert"), 1, 1, Qfalse); /* * Context private key */ rb_attr(cSSLContext, rb_intern("key"), 1, 1, Qfalse); /* * A certificate or Array of certificates that will be sent to the client. */ rb_attr(cSSLContext, rb_intern("client_ca"), 1, 1, Qfalse); /* * The path to a file containing a PEM-format CA certificate */ rb_attr(cSSLContext, rb_intern("ca_file"), 1, 1, Qfalse); /* * The path to a directory containing CA certificates in PEM format. * * Files are looked up by subject's X509 name's hash value. */ rb_attr(cSSLContext, rb_intern("ca_path"), 1, 1, Qfalse); /* * Maximum session lifetime in seconds. */ rb_attr(cSSLContext, rb_intern("timeout"), 1, 1, Qfalse); /* * Session verification mode. * * Valid modes are VERIFY_NONE, VERIFY_PEER, VERIFY_CLIENT_ONCE, * VERIFY_FAIL_IF_NO_PEER_CERT and defined on OpenSSL::SSL * * The default mode is VERIFY_NONE, which does not perform any verification * at all. * * See SSL_CTX_set_verify(3) for details. */ rb_attr(cSSLContext, rb_intern("verify_mode"), 1, 1, Qfalse); /* * Number of CA certificates to walk when verifying a certificate chain. */ rb_attr(cSSLContext, rb_intern("verify_depth"), 1, 1, Qfalse); /* * A callback for additional certificate verification. The callback is * invoked for each certificate in the chain. * * The callback is invoked with two values. +preverify_ok+ indicates * indicates if the verification was passed (true) or not (false). * +store_context+ is an OpenSSL::X509::StoreContext containing the * context used for certificate verification. * * If the callback returns false, the chain verification is immediately * stopped and a bad_certificate alert is then sent. */ rb_attr(cSSLContext, rb_intern("verify_callback"), 1, 1, Qfalse); /* * Whether to check the server certificate is valid for the hostname. * * In order to make this work, verify_mode must be set to VERIFY_PEER and * the server hostname must be given by OpenSSL::SSL::SSLSocket#hostname=. */ rb_attr(cSSLContext, rb_intern("verify_hostname"), 1, 1, Qfalse); /* * An OpenSSL::X509::Store used for certificate verification. */ rb_attr(cSSLContext, rb_intern("cert_store"), 1, 1, Qfalse); /* * An Array of extra X509 certificates to be added to the certificate * chain. */ rb_attr(cSSLContext, rb_intern("extra_chain_cert"), 1, 1, Qfalse); /* * A callback invoked when a client certificate is requested by a server * and no certificate has been set. * * The callback is invoked with a Session and must return an Array * containing an OpenSSL::X509::Certificate and an OpenSSL::PKey. If any * other value is returned the handshake is suspended. */ rb_attr(cSSLContext, rb_intern("client_cert_cb"), 1, 1, Qfalse); #if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) /* * A callback invoked when ECDH 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 is deprecated. This does not work with recent versions of * OpenSSL. Use OpenSSL::SSL::SSLContext#ecdh_curves= instead. */ rb_attr(cSSLContext, rb_intern("tmp_ecdh_callback"), 1, 1, Qfalse); #endif /* * Sets the context in which a session can be reused. This allows * sessions for multiple applications to be distinguished, for example, by * name. */ rb_attr(cSSLContext, rb_intern("session_id_context"), 1, 1, Qfalse); /* * A callback invoked on a server when a session is proposed by the client * but the session could not be found in the server's internal cache. * * The callback is invoked with the SSLSocket and session id. The * callback may return a Session from an external cache. */ rb_attr(cSSLContext, rb_intern("session_get_cb"), 1, 1, Qfalse); /* * A callback invoked when a new session was negotiated. * * The callback is invoked with an SSLSocket. If false is returned the * session will be removed from the internal cache. */ rb_attr(cSSLContext, rb_intern("session_new_cb"), 1, 1, Qfalse); /* * A callback invoked when a session is removed from the internal cache. * * The callback is invoked with an SSLContext and a Session. * * IMPORTANT NOTE: It is currently not possible to use this safely in a * multi-threaded application. The callback is called inside a global lock * and it can randomly cause deadlock on Ruby thread switching. */ rb_attr(cSSLContext, rb_intern("session_remove_cb"), 1, 1, Qfalse); #ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qtrue); #else rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qfalse); #endif #ifdef TLS_DH_anon_WITH_AES_256_GCM_SHA384 rb_define_const(mSSLExtConfig, "TLS_DH_anon_WITH_AES_256_GCM_SHA384", Qtrue); #else rb_define_const(mSSLExtConfig, "TLS_DH_anon_WITH_AES_256_GCM_SHA384", Qfalse); #endif /* * A callback invoked whenever a new handshake is initiated. May be used * to disable renegotiation entirely. * * The callback is invoked with the active SSLSocket. The callback's * return value is irrelevant, normal return indicates "approval" of the * renegotiation and will continue the process. To forbid renegotiation * and to cancel the process, an Error may be raised within the callback. * * === Disable client renegotiation * * When running a server, it is often desirable to disable client * renegotiation entirely. You may use a callback as follows to implement * this feature: * * num_handshakes = 0 * ctx.renegotiation_cb = lambda do |ssl| * num_handshakes += 1 * raise RuntimeError.new("Client renegotiation disabled") if num_handshakes > 1 * end */ rb_attr(cSSLContext, rb_intern("renegotiation_cb"), 1, 1, Qfalse); #ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB /* * An Enumerable of Strings. Each String represents a protocol to be * advertised as the list of supported protocols for Next Protocol * Negotiation. Supported in OpenSSL 1.0.1 and higher. Has no effect * on the client side. If not set explicitly, the NPN extension will * not be sent by the server in the handshake. * * === Example * * ctx.npn_protocols = ["http/1.1", "spdy/2"] */ rb_attr(cSSLContext, rb_intern("npn_protocols"), 1, 1, Qfalse); /* * A callback invoked on the client side when the client needs to select * a protocol from the list sent by the server. Supported in OpenSSL 1.0.1 * and higher. The client MUST select a protocol of those advertised by * the server. If none is acceptable, raising an error in the callback * will cause the handshake to fail. Not setting this callback explicitly * means not supporting the NPN extension on the client - any protocols * advertised by the server will be ignored. * * === Example * * ctx.npn_select_cb = lambda do |protocols| * # inspect the protocols and select one * protocols.first * end */ rb_attr(cSSLContext, rb_intern("npn_select_cb"), 1, 1, Qfalse); #endif #ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB /* * An Enumerable of Strings. Each String represents a protocol to be * advertised as the list of supported protocols for Application-Layer * Protocol Negotiation. Supported in OpenSSL 1.0.2 and higher. Has no * effect on the server side. If not set explicitly, the ALPN extension will * not be included in the handshake. * * === Example * * ctx.alpn_protocols = ["http/1.1", "spdy/2", "h2"] */ rb_attr(cSSLContext, rb_intern("alpn_protocols"), 1, 1, Qfalse); /* * A callback invoked on the server side when the server needs to select * a protocol from the list sent by the client. Supported in OpenSSL 1.0.2 * and higher. The callback must return a protocol of those advertised by * the client. If none is acceptable, raising an error in the callback * will cause the handshake to fail. Not setting this callback explicitly * means not supporting the ALPN extension on the server - any protocols * advertised by the client will be ignored. * * === Example * * ctx.alpn_select_cb = lambda do |protocols| * # inspect the protocols and select one * protocols.first * end */ rb_attr(cSSLContext, rb_intern("alpn_select_cb"), 1, 1, Qfalse); #endif rb_define_alias(cSSLContext, "ssl_timeout", "timeout"); rb_define_alias(cSSLContext, "ssl_timeout=", "timeout="); rb_define_method(cSSLContext, "ssl_version=", ossl_sslctx_set_ssl_version, 1); rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0); rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1); rb_define_method(cSSLContext, "ecdh_curves=", ossl_sslctx_set_ecdh_curves, 1); rb_define_method(cSSLContext, "security_level", ossl_sslctx_get_security_level, 0); rb_define_method(cSSLContext, "security_level=", ossl_sslctx_set_security_level, 1); rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0); rb_define_alias(cSSLContext, "freeze", "setup"); /* * No session caching for client or server */ rb_define_const(cSSLContext, "SESSION_CACHE_OFF", LONG2NUM(SSL_SESS_CACHE_OFF)); /* * Client sessions are added to the session cache */ rb_define_const(cSSLContext, "SESSION_CACHE_CLIENT", LONG2NUM(SSL_SESS_CACHE_CLIENT)); /* doesn't actually do anything in 0.9.8e */ /* * Server sessions are added to the session cache */ rb_define_const(cSSLContext, "SESSION_CACHE_SERVER", LONG2NUM(SSL_SESS_CACHE_SERVER)); /* * Both client and server sessions are added to the session cache */ rb_define_const(cSSLContext, "SESSION_CACHE_BOTH", LONG2NUM(SSL_SESS_CACHE_BOTH)); /* no different than CACHE_SERVER in 0.9.8e */ /* * Normally the session cache is checked for expired sessions every 255 * connections. Since this may lead to a delay that cannot be controlled, * the automatic flushing may be disabled and #flush_sessions can be * called explicitly. */ rb_define_const(cSSLContext, "SESSION_CACHE_NO_AUTO_CLEAR", LONG2NUM(SSL_SESS_CACHE_NO_AUTO_CLEAR)); /* * Always perform external lookups of sessions even if they are in the * internal cache. * * This flag has no effect on clients */ rb_define_const(cSSLContext, "SESSION_CACHE_NO_INTERNAL_LOOKUP", LONG2NUM(SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)); /* * Never automatically store sessions in the internal store. */ rb_define_const(cSSLContext, "SESSION_CACHE_NO_INTERNAL_STORE", LONG2NUM(SSL_SESS_CACHE_NO_INTERNAL_STORE)); /* * Enables both SESSION_CACHE_NO_INTERNAL_LOOKUP and * SESSION_CACHE_NO_INTERNAL_STORE. */ rb_define_const(cSSLContext, "SESSION_CACHE_NO_INTERNAL", LONG2NUM(SSL_SESS_CACHE_NO_INTERNAL)); rb_define_method(cSSLContext, "session_add", ossl_sslctx_session_add, 1); rb_define_method(cSSLContext, "session_remove", ossl_sslctx_session_remove, 1); rb_define_method(cSSLContext, "session_cache_mode", ossl_sslctx_get_session_cache_mode, 0); rb_define_method(cSSLContext, "session_cache_mode=", ossl_sslctx_set_session_cache_mode, 1); rb_define_method(cSSLContext, "session_cache_size", ossl_sslctx_get_session_cache_size, 0); rb_define_method(cSSLContext, "session_cache_size=", ossl_sslctx_set_session_cache_size, 1); rb_define_method(cSSLContext, "session_cache_stats", ossl_sslctx_get_session_cache_stats, 0); rb_define_method(cSSLContext, "flush_sessions", ossl_sslctx_flush_sessions, -1); rb_define_method(cSSLContext, "options", ossl_sslctx_get_options, 0); rb_define_method(cSSLContext, "options=", ossl_sslctx_set_options, 1); ary = rb_ary_new2(numberof(ossl_ssl_method_tab)); for (i = 0; i < numberof(ossl_ssl_method_tab); i++) { rb_ary_push(ary, ID2SYM(rb_intern(ossl_ssl_method_tab[i].name))); } rb_obj_freeze(ary); /* The list of available SSL/TLS methods */ rb_define_const(cSSLContext, "METHODS", ary); /* * Document-class: OpenSSL::SSL::SSLSocket */ cSSLSocket = rb_define_class_under(mSSL, "SSLSocket", rb_cObject); #ifdef OPENSSL_NO_SOCK rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qtrue); rb_define_method(cSSLSocket, "initialize", rb_f_notimplement, -1); #else rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qfalse); rb_define_alloc_func(cSSLSocket, ossl_ssl_s_alloc); rb_define_method(cSSLSocket, "initialize", ossl_ssl_initialize, -1); rb_undef_method(cSSLSocket, "initialize_copy"); rb_define_method(cSSLSocket, "connect", ossl_ssl_connect, 0); rb_define_method(cSSLSocket, "connect_nonblock", ossl_ssl_connect_nonblock, -1); rb_define_method(cSSLSocket, "accept", ossl_ssl_accept, 0); rb_define_method(cSSLSocket, "accept_nonblock", ossl_ssl_accept_nonblock, -1); rb_define_method(cSSLSocket, "sysread", ossl_ssl_read, -1); rb_define_private_method(cSSLSocket, "sysread_nonblock", ossl_ssl_read_nonblock, -1); rb_define_method(cSSLSocket, "syswrite", ossl_ssl_write, 1); rb_define_private_method(cSSLSocket, "syswrite_nonblock", ossl_ssl_write_nonblock, -1); rb_define_private_method(cSSLSocket, "stop", ossl_ssl_stop, 0); rb_define_method(cSSLSocket, "cert", ossl_ssl_get_cert, 0); rb_define_method(cSSLSocket, "peer_cert", ossl_ssl_get_peer_cert, 0); rb_define_method(cSSLSocket, "peer_cert_chain", ossl_ssl_get_peer_cert_chain, 0); rb_define_method(cSSLSocket, "ssl_version", ossl_ssl_get_version, 0); rb_define_method(cSSLSocket, "cipher", ossl_ssl_get_cipher, 0); rb_define_method(cSSLSocket, "state", ossl_ssl_get_state, 0); rb_define_method(cSSLSocket, "pending", ossl_ssl_pending, 0); rb_define_method(cSSLSocket, "session_reused?", ossl_ssl_session_reused, 0); /* implementation of OpenSSL::SSL::SSLSocket#session is in lib/openssl/ssl.rb */ rb_define_method(cSSLSocket, "session=", ossl_ssl_set_session, 1); rb_define_method(cSSLSocket, "verify_result", ossl_ssl_get_verify_result, 0); rb_define_method(cSSLSocket, "client_ca", ossl_ssl_get_client_ca_list, 0); #ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME /* #hostname is defined in lib/openssl/ssl.rb */ rb_define_method(cSSLSocket, "hostname=", ossl_ssl_set_hostname, 1); #endif # ifdef HAVE_SSL_GET_SERVER_TMP_KEY rb_define_method(cSSLSocket, "tmp_key", ossl_ssl_tmp_key, 0); # endif # ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB rb_define_method(cSSLSocket, "alpn_protocol", ossl_ssl_alpn_protocol, 0); # endif # ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0); # endif #endif #define ossl_ssl_def_const(x) rb_define_const(mSSL, #x, LONG2NUM(SSL_##x)) ossl_ssl_def_const(VERIFY_NONE); ossl_ssl_def_const(VERIFY_PEER); ossl_ssl_def_const(VERIFY_FAIL_IF_NO_PEER_CERT); ossl_ssl_def_const(VERIFY_CLIENT_ONCE); /* Introduce constants included in OP_ALL. These constants are mostly for * unset some bits in OP_ALL such as; * ctx.options = OP_ALL & ~OP_DONT_INSERT_EMPTY_FRAGMENTS */ ossl_ssl_def_const(OP_MICROSOFT_SESS_ID_BUG); ossl_ssl_def_const(OP_NETSCAPE_CHALLENGE_BUG); ossl_ssl_def_const(OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG); ossl_ssl_def_const(OP_SSLREF2_REUSE_CERT_TYPE_BUG); ossl_ssl_def_const(OP_MICROSOFT_BIG_SSLV3_BUFFER); ossl_ssl_def_const(OP_MSIE_SSLV2_RSA_PADDING); ossl_ssl_def_const(OP_SSLEAY_080_CLIENT_DH_BUG); ossl_ssl_def_const(OP_TLS_D5_BUG); ossl_ssl_def_const(OP_TLS_BLOCK_PADDING_BUG); ossl_ssl_def_const(OP_DONT_INSERT_EMPTY_FRAGMENTS); ossl_ssl_def_const(OP_ALL); ossl_ssl_def_const(OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); ossl_ssl_def_const(OP_SINGLE_ECDH_USE); ossl_ssl_def_const(OP_SINGLE_DH_USE); ossl_ssl_def_const(OP_EPHEMERAL_RSA); ossl_ssl_def_const(OP_CIPHER_SERVER_PREFERENCE); ossl_ssl_def_const(OP_TLS_ROLLBACK_BUG); ossl_ssl_def_const(OP_NO_SSLv2); ossl_ssl_def_const(OP_NO_SSLv3); ossl_ssl_def_const(OP_NO_TLSv1); #if defined(SSL_OP_NO_TLSv1_1) ossl_ssl_def_const(OP_NO_TLSv1_1); #endif #if defined(SSL_OP_NO_TLSv1_2) ossl_ssl_def_const(OP_NO_TLSv1_2); #endif #if defined(SSL_OP_NO_TICKET) ossl_ssl_def_const(OP_NO_TICKET); #endif #if defined(SSL_OP_NO_COMPRESSION) ossl_ssl_def_const(OP_NO_COMPRESSION); #endif ossl_ssl_def_const(OP_PKCS1_CHECK_1); ossl_ssl_def_const(OP_PKCS1_CHECK_2); ossl_ssl_def_const(OP_NETSCAPE_CA_DN_BUG); ossl_ssl_def_const(OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG); sym_exception = ID2SYM(rb_intern("exception")); sym_wait_readable = ID2SYM(rb_intern("wait_readable")); sym_wait_writable = ID2SYM(rb_intern("wait_writable")); id_tmp_dh_callback = rb_intern("tmp_dh_callback"); id_tmp_ecdh_callback = rb_intern("tmp_ecdh_callback"); id_npn_protocols_encoded = rb_intern("npn_protocols_encoded"); #define DefIVarID(name) do \ id_i_##name = rb_intern("@"#name); while (0) DefIVarID(cert_store); DefIVarID(ca_file); DefIVarID(ca_path); DefIVarID(verify_mode); DefIVarID(verify_depth); DefIVarID(verify_callback); DefIVarID(client_ca); DefIVarID(renegotiation_cb); DefIVarID(cert); DefIVarID(key); DefIVarID(extra_chain_cert); DefIVarID(client_cert_cb); DefIVarID(tmp_ecdh_callback); DefIVarID(timeout); DefIVarID(session_id_context); DefIVarID(session_get_cb); DefIVarID(session_new_cb); DefIVarID(session_remove_cb); DefIVarID(npn_select_cb); DefIVarID(npn_protocols); DefIVarID(alpn_protocols); DefIVarID(alpn_select_cb); DefIVarID(servername_cb); DefIVarID(verify_hostname); DefIVarID(io); DefIVarID(context); DefIVarID(hostname); } openssl-2.0.9/ext/openssl/ossl_ssl.h000066400000000000000000000020431336157045000175050ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_SSL_H_) #define _OSSL_SSL_H_ #define GetSSL(obj, ssl) do { \ TypedData_Get_Struct((obj), SSL, &ossl_ssl_type, (ssl)); \ if (!(ssl)) { \ ossl_raise(rb_eRuntimeError, "SSL is not initialized"); \ } \ } while (0) #define GetSSLSession(obj, sess) do { \ TypedData_Get_Struct((obj), SSL_SESSION, &ossl_ssl_session_type, (sess)); \ if (!(sess)) { \ ossl_raise(rb_eRuntimeError, "SSL Session wasn't initialized."); \ } \ } while (0) #define SafeGetSSLSession(obj, sess) do { \ OSSL_Check_Kind((obj), cSSLSession); \ GetSSLSession((obj), (sess)); \ } while (0) extern const rb_data_type_t ossl_ssl_type; extern const rb_data_type_t ossl_ssl_session_type; extern VALUE mSSL; extern VALUE cSSLSocket; extern VALUE cSSLSession; void Init_ossl_ssl(void); void Init_ossl_ssl_session(void); #endif /* _OSSL_SSL_H_ */ openssl-2.0.9/ext/openssl/ossl_ssl_session.c000066400000000000000000000162201336157045000212450ustar00rootroot00000000000000/* * Copyright (C) 2004-2007 Technorama Ltd. */ #include "ossl.h" VALUE cSSLSession; static VALUE eSSLSession; static void ossl_ssl_session_free(void *ptr) { SSL_SESSION_free(ptr); } const rb_data_type_t ossl_ssl_session_type = { "OpenSSL/SSL/Session", { 0, ossl_ssl_session_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static VALUE ossl_ssl_session_alloc(VALUE klass) { return TypedData_Wrap_Struct(klass, &ossl_ssl_session_type, NULL); } /* * call-seq: * Session.new(ssl_socket) -> Session * Session.new(string) -> Session * * Creates a new Session object from an instance of SSLSocket or DER/PEM encoded * String. */ static VALUE ossl_ssl_session_initialize(VALUE self, VALUE arg1) { SSL_SESSION *ctx = NULL; if (RDATA(self)->data) ossl_raise(eSSLSession, "SSL Session already initialized"); if (rb_obj_is_instance_of(arg1, cSSLSocket)) { SSL *ssl; GetSSL(arg1, ssl); if ((ctx = SSL_get1_session(ssl)) == NULL) ossl_raise(eSSLSession, "no session available"); } else { BIO *in = ossl_obj2bio(&arg1); ctx = PEM_read_bio_SSL_SESSION(in, NULL, NULL, NULL); if (!ctx) { OSSL_BIO_reset(in); ctx = d2i_SSL_SESSION_bio(in, NULL); } BIO_free(in); if (!ctx) ossl_raise(rb_eArgError, "unknown type"); } /* should not happen */ if (ctx == NULL) ossl_raise(eSSLSession, "ctx not set - internal error"); RDATA(self)->data = ctx; return self; } static VALUE ossl_ssl_session_initialize_copy(VALUE self, VALUE other) { SSL_SESSION *sess, *sess_other, *sess_new; rb_check_frozen(self); sess = RTYPEDDATA_DATA(self); /* XXX */ SafeGetSSLSession(other, sess_other); sess_new = ASN1_dup((i2d_of_void *)i2d_SSL_SESSION, (d2i_of_void *)d2i_SSL_SESSION, (char *)sess_other); if (!sess_new) ossl_raise(eSSLSession, "ASN1_dup"); RTYPEDDATA_DATA(self) = sess_new; SSL_SESSION_free(sess); return self; } #if !defined(HAVE_SSL_SESSION_CMP) int ossl_SSL_SESSION_cmp(const SSL_SESSION *a, const SSL_SESSION *b) { unsigned int a_len; const unsigned char *a_sid = SSL_SESSION_get_id(a, &a_len); unsigned int b_len; const unsigned char *b_sid = SSL_SESSION_get_id(b, &b_len); if (SSL_SESSION_get_protocol_version(a) != SSL_SESSION_get_protocol_version(b)) return 1; if (a_len != b_len) return 1; return CRYPTO_memcmp(a_sid, b_sid, a_len); } #define SSL_SESSION_cmp(a, b) ossl_SSL_SESSION_cmp(a, b) #endif /* * call-seq: * session1 == session2 -> boolean * * Returns true if the two Session is the same, false if not. */ static VALUE ossl_ssl_session_eq(VALUE val1, VALUE val2) { SSL_SESSION *ctx1, *ctx2; GetSSLSession(val1, ctx1); SafeGetSSLSession(val2, ctx2); switch (SSL_SESSION_cmp(ctx1, ctx2)) { case 0: return Qtrue; default: return Qfalse; } } /* * call-seq: * session.time -> Time * * Returns the time at which the session was established. */ static VALUE ossl_ssl_session_get_time(VALUE self) { SSL_SESSION *ctx; long t; GetSSLSession(self, ctx); t = SSL_SESSION_get_time(ctx); if (t == 0) return Qnil; return rb_funcall(rb_cTime, rb_intern("at"), 1, LONG2NUM(t)); } /* * call-seq: * session.timeout -> Integer * * Returns the timeout value set for the session, in seconds from the * established time. * */ static VALUE ossl_ssl_session_get_timeout(VALUE self) { SSL_SESSION *ctx; long t; GetSSLSession(self, ctx); t = SSL_SESSION_get_timeout(ctx); return LONG2NUM(t); } /* * call-seq: * session.time = time * session.time = integer * * Sets start time of the session. Time resolution is in seconds. * */ static VALUE ossl_ssl_session_set_time(VALUE self, VALUE time_v) { SSL_SESSION *ctx; long t; GetSSLSession(self, ctx); if (rb_obj_is_instance_of(time_v, rb_cTime)) { time_v = rb_funcall(time_v, rb_intern("to_i"), 0); } t = NUM2LONG(time_v); SSL_SESSION_set_time(ctx, t); return ossl_ssl_session_get_time(self); } /* * call-seq: * session.timeout = integer * * Sets how long until the session expires in seconds. */ static VALUE ossl_ssl_session_set_timeout(VALUE self, VALUE time_v) { SSL_SESSION *ctx; long t; GetSSLSession(self, ctx); t = NUM2LONG(time_v); SSL_SESSION_set_timeout(ctx, t); return ossl_ssl_session_get_timeout(self); } /* * call-seq: * session.id -> String * * Returns the Session ID. */ static VALUE ossl_ssl_session_get_id(VALUE self) { SSL_SESSION *ctx; const unsigned char *p = NULL; unsigned int i = 0; GetSSLSession(self, ctx); p = SSL_SESSION_get_id(ctx, &i); return rb_str_new((const char *) p, i); } /* * call-seq: * session.to_der -> String * * Returns an ASN1 encoded String that contains the Session object. */ static VALUE ossl_ssl_session_to_der(VALUE self) { SSL_SESSION *ctx; unsigned char *p; int len; VALUE str; GetSSLSession(self, ctx); len = i2d_SSL_SESSION(ctx, NULL); if (len <= 0) { ossl_raise(eSSLSession, "i2d_SSL_SESSION"); } str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); i2d_SSL_SESSION(ctx, &p); ossl_str_adjust(str, p); return str; } /* * call-seq: * session.to_pem -> String * * Returns a PEM encoded String that contains the Session object. */ static VALUE ossl_ssl_session_to_pem(VALUE self) { SSL_SESSION *ctx; BIO *out; GetSSLSession(self, ctx); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eSSLSession, "BIO_s_mem()"); } if (!PEM_write_bio_SSL_SESSION(out, ctx)) { BIO_free(out); ossl_raise(eSSLSession, "SSL_SESSION_print()"); } return ossl_membio2str(out); } /* * call-seq: * session.to_text -> String * * Shows everything in the Session object. This is for diagnostic purposes. */ static VALUE ossl_ssl_session_to_text(VALUE self) { SSL_SESSION *ctx; BIO *out; GetSSLSession(self, ctx); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eSSLSession, "BIO_s_mem()"); } if (!SSL_SESSION_print(out, ctx)) { BIO_free(out); ossl_raise(eSSLSession, "SSL_SESSION_print()"); } return ossl_membio2str(out); } void Init_ossl_ssl_session(void) { #if 0 mOSSL = rb_define_module("OpenSSL"); mSSL = rb_define_module_under(mOSSL, "SSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); #endif cSSLSession = rb_define_class_under(mSSL, "Session", rb_cObject); eSSLSession = rb_define_class_under(cSSLSession, "SessionError", eOSSLError); rb_define_alloc_func(cSSLSession, ossl_ssl_session_alloc); rb_define_method(cSSLSession, "initialize", ossl_ssl_session_initialize, 1); rb_define_copy_func(cSSLSession, ossl_ssl_session_initialize_copy); rb_define_method(cSSLSession, "==", ossl_ssl_session_eq, 1); rb_define_method(cSSLSession, "time", ossl_ssl_session_get_time, 0); rb_define_method(cSSLSession, "time=", ossl_ssl_session_set_time, 1); rb_define_method(cSSLSession, "timeout", ossl_ssl_session_get_timeout, 0); rb_define_method(cSSLSession, "timeout=", ossl_ssl_session_set_timeout, 1); rb_define_method(cSSLSession, "id", ossl_ssl_session_get_id, 0); rb_define_method(cSSLSession, "to_der", ossl_ssl_session_to_der, 0); rb_define_method(cSSLSession, "to_pem", ossl_ssl_session_to_pem, 0); rb_define_method(cSSLSession, "to_text", ossl_ssl_session_to_text, 0); } openssl-2.0.9/ext/openssl/ossl_version.h000066400000000000000000000005201336157045000203670ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_VERSION_H_) #define _OSSL_VERSION_H_ #define OSSL_VERSION "2.0.9" #endif /* _OSSL_VERSION_H_ */ openssl-2.0.9/ext/openssl/ossl_x509.c000066400000000000000000000154201336157045000174070ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" VALUE mX509; #define DefX509Const(x) rb_define_const(mX509, #x, INT2NUM(X509_##x)) #define DefX509Default(x,i) \ rb_define_const(mX509, "DEFAULT_" #x, rb_str_new2(X509_get_default_##i())) ASN1_TIME * ossl_x509_time_adjust(ASN1_TIME *s, VALUE time) { time_t sec; #if defined(HAVE_ASN1_TIME_ADJ) int off_days; ossl_time_split(time, &sec, &off_days); return X509_time_adj_ex(s, off_days, 0, &sec); #else sec = time_to_time_t(time); return X509_time_adj(s, 0, &sec); #endif } void Init_ossl_x509(void) { #if 0 mOSSL = rb_define_module("OpenSSL"); #endif mX509 = rb_define_module_under(mOSSL, "X509"); Init_ossl_x509attr(); Init_ossl_x509cert(); Init_ossl_x509crl(); Init_ossl_x509ext(); Init_ossl_x509name(); Init_ossl_x509req(); Init_ossl_x509revoked(); Init_ossl_x509store(); DefX509Const(V_OK); DefX509Const(V_ERR_UNABLE_TO_GET_ISSUER_CERT); DefX509Const(V_ERR_UNABLE_TO_GET_CRL); DefX509Const(V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE); DefX509Const(V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE); DefX509Const(V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY); DefX509Const(V_ERR_CERT_SIGNATURE_FAILURE); DefX509Const(V_ERR_CRL_SIGNATURE_FAILURE); DefX509Const(V_ERR_CERT_NOT_YET_VALID); DefX509Const(V_ERR_CERT_HAS_EXPIRED); DefX509Const(V_ERR_CRL_NOT_YET_VALID); DefX509Const(V_ERR_CRL_HAS_EXPIRED); DefX509Const(V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD); DefX509Const(V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD); DefX509Const(V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD); DefX509Const(V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); DefX509Const(V_ERR_OUT_OF_MEM); DefX509Const(V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT); DefX509Const(V_ERR_SELF_SIGNED_CERT_IN_CHAIN); DefX509Const(V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY); DefX509Const(V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE); DefX509Const(V_ERR_CERT_CHAIN_TOO_LONG); DefX509Const(V_ERR_CERT_REVOKED); DefX509Const(V_ERR_INVALID_CA); DefX509Const(V_ERR_PATH_LENGTH_EXCEEDED); DefX509Const(V_ERR_INVALID_PURPOSE); DefX509Const(V_ERR_CERT_UNTRUSTED); DefX509Const(V_ERR_CERT_REJECTED); DefX509Const(V_ERR_SUBJECT_ISSUER_MISMATCH); DefX509Const(V_ERR_AKID_SKID_MISMATCH); DefX509Const(V_ERR_AKID_ISSUER_SERIAL_MISMATCH); DefX509Const(V_ERR_KEYUSAGE_NO_CERTSIGN); DefX509Const(V_ERR_APPLICATION_VERIFICATION); /* Set by Store#flags= and StoreContext#flags=. Enables CRL checking for the * certificate chain leaf. */ DefX509Const(V_FLAG_CRL_CHECK); /* Set by Store#flags= and StoreContext#flags=. Enables CRL checking for all * certificates in the certificate chain */ DefX509Const(V_FLAG_CRL_CHECK_ALL); /* Set by Store#flags= and StoreContext#flags=. Disables critical extension * checking. */ DefX509Const(V_FLAG_IGNORE_CRITICAL); /* Set by Store#flags= and StoreContext#flags=. Disables workarounds for * broken certificates. */ DefX509Const(V_FLAG_X509_STRICT); /* Set by Store#flags= and StoreContext#flags=. Enables proxy certificate * verification. */ DefX509Const(V_FLAG_ALLOW_PROXY_CERTS); /* Set by Store#flags= and StoreContext#flags=. Enables certificate policy * constraints checking. */ DefX509Const(V_FLAG_POLICY_CHECK); /* Set by Store#flags= and StoreContext#flags=. * Implies V_FLAG_POLICY_CHECK */ DefX509Const(V_FLAG_EXPLICIT_POLICY); /* Set by Store#flags= and StoreContext#flags=. * Implies V_FLAG_POLICY_CHECK */ DefX509Const(V_FLAG_INHIBIT_ANY); /* Set by Store#flags= and StoreContext#flags=. * Implies V_FLAG_POLICY_CHECK */ DefX509Const(V_FLAG_INHIBIT_MAP); /* Set by Store#flags= and StoreContext#flags=. */ DefX509Const(V_FLAG_NOTIFY_POLICY); #if defined(X509_V_FLAG_EXTENDED_CRL_SUPPORT) /* Set by Store#flags= and StoreContext#flags=. Enables some additional * features including support for indirect signed CRLs. */ DefX509Const(V_FLAG_EXTENDED_CRL_SUPPORT); #endif #if defined(X509_V_FLAG_USE_DELTAS) /* Set by Store#flags= and StoreContext#flags=. Uses delta CRLs. If not * specified, deltas are ignored. */ DefX509Const(V_FLAG_USE_DELTAS); #endif #if defined(X509_V_FLAG_CHECK_SS_SIGNATURE) /* Set by Store#flags= and StoreContext#flags=. Enables checking of the * signature of the root self-signed CA. */ DefX509Const(V_FLAG_CHECK_SS_SIGNATURE); #endif #if defined(X509_V_FLAG_TRUSTED_FIRST) /* Set by Store#flags= and StoreContext#flags=. When constructing a * certificate chain, search the Store first for the issuer certificate. * Enabled by default in OpenSSL >= 1.1.0. */ DefX509Const(V_FLAG_TRUSTED_FIRST); #endif #if defined(X509_V_FLAG_NO_ALT_CHAINS) /* Set by Store#flags= and StoreContext#flags=. Suppresses searching for * a alternative chain. No effect in OpenSSL >= 1.1.0. */ DefX509Const(V_FLAG_NO_ALT_CHAINS); #endif #if defined(X509_V_FLAG_NO_CHECK_TIME) /* Set by Store#flags= and StoreContext#flags=. Suppresses checking the * validity period of certificates and CRLs. No effect when the current * time is explicitly set by Store#time= or StoreContext#time=. */ DefX509Const(V_FLAG_NO_CHECK_TIME); #endif /* Set by Store#purpose=. SSL/TLS client. */ DefX509Const(PURPOSE_SSL_CLIENT); /* Set by Store#purpose=. SSL/TLS server. */ DefX509Const(PURPOSE_SSL_SERVER); /* Set by Store#purpose=. Netscape SSL server. */ DefX509Const(PURPOSE_NS_SSL_SERVER); /* Set by Store#purpose=. S/MIME signing. */ DefX509Const(PURPOSE_SMIME_SIGN); /* Set by Store#purpose=. S/MIME encryption. */ DefX509Const(PURPOSE_SMIME_ENCRYPT); /* Set by Store#purpose=. CRL signing */ DefX509Const(PURPOSE_CRL_SIGN); /* Set by Store#purpose=. No checks. */ DefX509Const(PURPOSE_ANY); /* Set by Store#purpose=. OCSP helper. */ DefX509Const(PURPOSE_OCSP_HELPER); #if defined(X509_PURPOSE_TIMESTAMP_SIGN) /* Set by Store#purpose=. Time stamps signer. */ DefX509Const(PURPOSE_TIMESTAMP_SIGN); #endif DefX509Const(TRUST_COMPAT); DefX509Const(TRUST_SSL_CLIENT); DefX509Const(TRUST_SSL_SERVER); DefX509Const(TRUST_EMAIL); DefX509Const(TRUST_OBJECT_SIGN); DefX509Const(TRUST_OCSP_SIGN); DefX509Const(TRUST_OCSP_REQUEST); #if defined(X509_TRUST_TSA) DefX509Const(TRUST_TSA); #endif DefX509Default(CERT_AREA, cert_area); DefX509Default(CERT_DIR, cert_dir); DefX509Default(CERT_FILE, cert_file); DefX509Default(CERT_DIR_ENV, cert_dir_env); DefX509Default(CERT_FILE_ENV, cert_file_env); DefX509Default(PRIVATE_DIR, private_dir); } openssl-2.0.9/ext/openssl/ossl_x509.h000066400000000000000000000047511336157045000174210ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_X509_H_) #define _OSSL_X509_H_ /* * X509 main module */ extern VALUE mX509; /* * Converts the VALUE into Integer and set it to the ASN1_TIME. This is a * wrapper for X509_time_adj_ex() so passing NULL creates a new ASN1_TIME. * Note that the caller must check the NULL return. */ ASN1_TIME *ossl_x509_time_adjust(ASN1_TIME *, VALUE); void Init_ossl_x509(void); /* * X509Attr */ extern VALUE cX509Attr; extern VALUE eX509AttrError; VALUE ossl_x509attr_new(X509_ATTRIBUTE *); X509_ATTRIBUTE *GetX509AttrPtr(VALUE); void Init_ossl_x509attr(void); /* * X509Cert */ extern VALUE cX509Cert; extern VALUE eX509CertError; VALUE ossl_x509_new(X509 *); VALUE ossl_x509_new_from_file(VALUE); X509 *GetX509CertPtr(VALUE); X509 *DupX509CertPtr(VALUE); void Init_ossl_x509cert(void); /* * X509CRL */ extern VALUE cX509CRL; extern VALUE eX509CRLError; VALUE ossl_x509crl_new(X509_CRL *); X509_CRL *GetX509CRLPtr(VALUE); X509_CRL *DupX509CRLPtr(VALUE); void Init_ossl_x509crl(void); /* * X509Extension */ extern VALUE cX509Ext; extern VALUE cX509ExtFactory; extern VALUE eX509ExtError; VALUE ossl_x509ext_new(X509_EXTENSION *); X509_EXTENSION *GetX509ExtPtr(VALUE); void Init_ossl_x509ext(void); /* * X509Name */ extern VALUE cX509Name; extern VALUE eX509NameError; VALUE ossl_x509name_new(X509_NAME *); X509_NAME *GetX509NamePtr(VALUE); void Init_ossl_x509name(void); /* * X509Request */ extern VALUE cX509Req; extern VALUE eX509ReqError; VALUE ossl_x509req_new(X509_REQ *); X509_REQ *GetX509ReqPtr(VALUE); X509_REQ *DupX509ReqPtr(VALUE); void Init_ossl_x509req(void); /* * X509Revoked */ extern VALUE cX509Rev; extern VALUE eX509RevError; VALUE ossl_x509revoked_new(X509_REVOKED *); X509_REVOKED *DupX509RevokedPtr(VALUE); void Init_ossl_x509revoked(void); /* * X509Store and X509StoreContext */ extern VALUE cX509Store; extern VALUE cX509StoreContext; extern VALUE eX509StoreError; VALUE ossl_x509store_new(X509_STORE *); X509_STORE *GetX509StorePtr(VALUE); X509_STORE *DupX509StorePtr(VALUE); X509_STORE_CTX *GetX509StCtxtPtr(VALUE); void Init_ossl_x509store(void); /* * Calls the verify callback Proc (the first parameter) with given pre-verify * result and the X509_STORE_CTX. */ int ossl_verify_cb_call(VALUE, int, X509_STORE_CTX *); #endif /* _OSSL_X509_H_ */ openssl-2.0.9/ext/openssl/ossl_x509attr.c000066400000000000000000000166311336157045000203070ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define NewX509Attr(klass) \ TypedData_Wrap_Struct((klass), &ossl_x509attr_type, 0) #define SetX509Attr(obj, attr) do { \ if (!(attr)) { \ ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (attr); \ } while (0) #define GetX509Attr(obj, attr) do { \ TypedData_Get_Struct((obj), X509_ATTRIBUTE, &ossl_x509attr_type, (attr)); \ if (!(attr)) { \ ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \ } \ } while (0) #define SafeGetX509Attr(obj, attr) do { \ OSSL_Check_Kind((obj), cX509Attr); \ GetX509Attr((obj), (attr)); \ } while (0) /* * Classes */ VALUE cX509Attr; VALUE eX509AttrError; static void ossl_x509attr_free(void *ptr) { X509_ATTRIBUTE_free(ptr); } static const rb_data_type_t ossl_x509attr_type = { "OpenSSL/X509/ATTRIBUTE", { 0, ossl_x509attr_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * Public */ VALUE ossl_x509attr_new(X509_ATTRIBUTE *attr) { X509_ATTRIBUTE *new; VALUE obj; obj = NewX509Attr(cX509Attr); if (!attr) { new = X509_ATTRIBUTE_new(); } else { new = X509_ATTRIBUTE_dup(attr); } if (!new) { ossl_raise(eX509AttrError, NULL); } SetX509Attr(obj, new); return obj; } X509_ATTRIBUTE * GetX509AttrPtr(VALUE obj) { X509_ATTRIBUTE *attr; SafeGetX509Attr(obj, attr); return attr; } /* * Private */ static VALUE ossl_x509attr_alloc(VALUE klass) { X509_ATTRIBUTE *attr; VALUE obj; obj = NewX509Attr(klass); if (!(attr = X509_ATTRIBUTE_new())) ossl_raise(eX509AttrError, NULL); SetX509Attr(obj, attr); return obj; } /* * call-seq: * Attribute.new(oid [, value]) => attr */ static VALUE ossl_x509attr_initialize(int argc, VALUE *argv, VALUE self) { VALUE oid, value; X509_ATTRIBUTE *attr, *x; const unsigned char *p; GetX509Attr(self, attr); if(rb_scan_args(argc, argv, "11", &oid, &value) == 1){ oid = ossl_to_der_if_possible(oid); StringValue(oid); p = (unsigned char *)RSTRING_PTR(oid); x = d2i_X509_ATTRIBUTE(&attr, &p, RSTRING_LEN(oid)); DATA_PTR(self) = attr; if(!x){ ossl_raise(eX509AttrError, NULL); } return self; } rb_funcall(self, rb_intern("oid="), 1, oid); rb_funcall(self, rb_intern("value="), 1, value); return self; } static VALUE ossl_x509attr_initialize_copy(VALUE self, VALUE other) { X509_ATTRIBUTE *attr, *attr_other, *attr_new; rb_check_frozen(self); GetX509Attr(self, attr); SafeGetX509Attr(other, attr_other); attr_new = X509_ATTRIBUTE_dup(attr_other); if (!attr_new) ossl_raise(eX509AttrError, "X509_ATTRIBUTE_dup"); SetX509Attr(self, attr_new); X509_ATTRIBUTE_free(attr); return self; } /* * call-seq: * attr.oid = string => string */ static VALUE ossl_x509attr_set_oid(VALUE self, VALUE oid) { X509_ATTRIBUTE *attr; ASN1_OBJECT *obj; char *s; GetX509Attr(self, attr); s = StringValueCStr(oid); obj = OBJ_txt2obj(s, 0); if(!obj) ossl_raise(eX509AttrError, NULL); if (!X509_ATTRIBUTE_set1_object(attr, obj)) { ASN1_OBJECT_free(obj); ossl_raise(eX509AttrError, "X509_ATTRIBUTE_set1_object"); } ASN1_OBJECT_free(obj); return oid; } /* * call-seq: * attr.oid => string */ static VALUE ossl_x509attr_get_oid(VALUE self) { X509_ATTRIBUTE *attr; ASN1_OBJECT *oid; BIO *out; VALUE ret; int nid; GetX509Attr(self, attr); oid = X509_ATTRIBUTE_get0_object(attr); if ((nid = OBJ_obj2nid(oid)) != NID_undef) ret = rb_str_new2(OBJ_nid2sn(nid)); else{ if (!(out = BIO_new(BIO_s_mem()))) ossl_raise(eX509AttrError, NULL); i2a_ASN1_OBJECT(out, oid); ret = ossl_membio2str(out); } return ret; } /* * call-seq: * attr.value = asn1 => asn1 */ static VALUE ossl_x509attr_set_value(VALUE self, VALUE value) { X509_ATTRIBUTE *attr; VALUE asn1_value; int i, asn1_tag; OSSL_Check_Kind(value, cASN1Data); asn1_tag = NUM2INT(rb_attr_get(value, rb_intern("@tag"))); asn1_value = rb_attr_get(value, rb_intern("@value")); if (asn1_tag != V_ASN1_SET) ossl_raise(eASN1Error, "argument must be ASN1::Set"); if (!RB_TYPE_P(asn1_value, T_ARRAY)) ossl_raise(eASN1Error, "ASN1::Set has non-array value"); GetX509Attr(self, attr); if (X509_ATTRIBUTE_count(attr)) { /* populated, reset first */ ASN1_OBJECT *obj = X509_ATTRIBUTE_get0_object(attr); X509_ATTRIBUTE *new_attr = X509_ATTRIBUTE_create_by_OBJ(NULL, obj, 0, NULL, -1); if (!new_attr) ossl_raise(eX509AttrError, NULL); SetX509Attr(self, new_attr); X509_ATTRIBUTE_free(attr); attr = new_attr; } for (i = 0; i < RARRAY_LEN(asn1_value); i++) { ASN1_TYPE *a1type = ossl_asn1_get_asn1type(RARRAY_AREF(asn1_value, i)); if (!X509_ATTRIBUTE_set1_data(attr, ASN1_TYPE_get(a1type), a1type->value.ptr, -1)) { ASN1_TYPE_free(a1type); ossl_raise(eX509AttrError, NULL); } ASN1_TYPE_free(a1type); } return value; } /* * call-seq: * attr.value => asn1 */ static VALUE ossl_x509attr_get_value(VALUE self) { X509_ATTRIBUTE *attr; STACK_OF(ASN1_TYPE) *sk; VALUE str; int i, count, len; unsigned char *p; GetX509Attr(self, attr); /* there is no X509_ATTRIBUTE_get0_set() :( */ if (!(sk = sk_ASN1_TYPE_new_null())) ossl_raise(eX509AttrError, "sk_new"); count = X509_ATTRIBUTE_count(attr); for (i = 0; i < count; i++) sk_ASN1_TYPE_push(sk, X509_ATTRIBUTE_get0_type(attr, i)); if ((len = i2d_ASN1_SET_ANY(sk, NULL)) <= 0) { sk_ASN1_TYPE_free(sk); ossl_raise(eX509AttrError, NULL); } str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_ASN1_SET_ANY(sk, &p) <= 0) { sk_ASN1_TYPE_free(sk); ossl_raise(eX509AttrError, NULL); } ossl_str_adjust(str, p); sk_ASN1_TYPE_free(sk); return rb_funcall(mASN1, rb_intern("decode"), 1, str); } /* * call-seq: * attr.to_der => string */ static VALUE ossl_x509attr_to_der(VALUE self) { X509_ATTRIBUTE *attr; VALUE str; int len; unsigned char *p; GetX509Attr(self, attr); if((len = i2d_X509_ATTRIBUTE(attr, NULL)) <= 0) ossl_raise(eX509AttrError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_X509_ATTRIBUTE(attr, &p) <= 0) ossl_raise(eX509AttrError, NULL); ossl_str_adjust(str, p); return str; } /* * X509_ATTRIBUTE init */ void Init_ossl_x509attr(void) { #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); mX509 = rb_define_module_under(mOSSL, "X509"); #endif eX509AttrError = rb_define_class_under(mX509, "AttributeError", eOSSLError); cX509Attr = rb_define_class_under(mX509, "Attribute", rb_cObject); rb_define_alloc_func(cX509Attr, ossl_x509attr_alloc); rb_define_method(cX509Attr, "initialize", ossl_x509attr_initialize, -1); rb_define_copy_func(cX509Attr, ossl_x509attr_initialize_copy); rb_define_method(cX509Attr, "oid=", ossl_x509attr_set_oid, 1); rb_define_method(cX509Attr, "oid", ossl_x509attr_get_oid, 0); rb_define_method(cX509Attr, "value=", ossl_x509attr_set_value, 1); rb_define_method(cX509Attr, "value", ossl_x509attr_get_value, 0); rb_define_method(cX509Attr, "to_der", ossl_x509attr_to_der, 0); } openssl-2.0.9/ext/openssl/ossl_x509cert.c000066400000000000000000000474261336157045000203000ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define NewX509(klass) \ TypedData_Wrap_Struct((klass), &ossl_x509_type, 0) #define SetX509(obj, x509) do { \ if (!(x509)) { \ ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (x509); \ } while (0) #define GetX509(obj, x509) do { \ TypedData_Get_Struct((obj), X509, &ossl_x509_type, (x509)); \ if (!(x509)) { \ ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \ } \ } while (0) #define SafeGetX509(obj, x509) do { \ OSSL_Check_Kind((obj), cX509Cert); \ GetX509((obj), (x509)); \ } while (0) /* * Classes */ VALUE cX509Cert; VALUE eX509CertError; static void ossl_x509_free(void *ptr) { X509_free(ptr); } static const rb_data_type_t ossl_x509_type = { "OpenSSL/X509", { 0, ossl_x509_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * Public */ VALUE ossl_x509_new(X509 *x509) { X509 *new; VALUE obj; obj = NewX509(cX509Cert); if (!x509) { new = X509_new(); } else { new = X509_dup(x509); } if (!new) { ossl_raise(eX509CertError, NULL); } SetX509(obj, new); return obj; } VALUE ossl_x509_new_from_file(VALUE filename) { X509 *x509; FILE *fp; VALUE obj; rb_check_safe_obj(filename); obj = NewX509(cX509Cert); if (!(fp = fopen(StringValueCStr(filename), "r"))) { ossl_raise(eX509CertError, "%s", strerror(errno)); } rb_fd_fix_cloexec(fileno(fp)); x509 = PEM_read_X509(fp, NULL, NULL, NULL); /* * prepare for DER... #if !defined(OPENSSL_NO_FP_API) if (!x509) { (void)ERR_get_error(); rewind(fp); x509 = d2i_X509_fp(fp, NULL); } #endif */ fclose(fp); if (!x509) { ossl_raise(eX509CertError, NULL); } SetX509(obj, x509); return obj; } X509 * GetX509CertPtr(VALUE obj) { X509 *x509; SafeGetX509(obj, x509); return x509; } X509 * DupX509CertPtr(VALUE obj) { X509 *x509; SafeGetX509(obj, x509); X509_up_ref(x509); return x509; } /* * Private */ static VALUE ossl_x509_alloc(VALUE klass) { X509 *x509; VALUE obj; obj = NewX509(klass); x509 = X509_new(); if (!x509) ossl_raise(eX509CertError, NULL); SetX509(obj, x509); return obj; } /* * call-seq: * Certificate.new => cert * Certificate.new(string) => cert */ static VALUE ossl_x509_initialize(int argc, VALUE *argv, VALUE self) { BIO *in; X509 *x509, *x = DATA_PTR(self); VALUE arg; if (rb_scan_args(argc, argv, "01", &arg) == 0) { /* create just empty X509Cert */ return self; } arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); x509 = PEM_read_bio_X509(in, &x, NULL, NULL); DATA_PTR(self) = x; if (!x509) { OSSL_BIO_reset(in); x509 = d2i_X509_bio(in, &x); DATA_PTR(self) = x; } BIO_free(in); if (!x509) ossl_raise(eX509CertError, NULL); return self; } static VALUE ossl_x509_copy(VALUE self, VALUE other) { X509 *a, *b, *x509; rb_check_frozen(self); if (self == other) return self; GetX509(self, a); SafeGetX509(other, b); x509 = X509_dup(b); if (!x509) ossl_raise(eX509CertError, NULL); DATA_PTR(self) = x509; X509_free(a); return self; } /* * call-seq: * cert.to_der => string */ static VALUE ossl_x509_to_der(VALUE self) { X509 *x509; VALUE str; long len; unsigned char *p; GetX509(self, x509); if ((len = i2d_X509(x509, NULL)) <= 0) ossl_raise(eX509CertError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_X509(x509, &p) <= 0) ossl_raise(eX509CertError, NULL); ossl_str_adjust(str, p); return str; } /* * call-seq: * cert.to_pem => string */ static VALUE ossl_x509_to_pem(VALUE self) { X509 *x509; BIO *out; VALUE str; GetX509(self, x509); out = BIO_new(BIO_s_mem()); if (!out) ossl_raise(eX509CertError, NULL); if (!PEM_write_bio_X509(out, x509)) { BIO_free(out); ossl_raise(eX509CertError, NULL); } str = ossl_membio2str(out); return str; } /* * call-seq: * cert.to_text => string */ static VALUE ossl_x509_to_text(VALUE self) { X509 *x509; BIO *out; VALUE str; GetX509(self, x509); out = BIO_new(BIO_s_mem()); if (!out) ossl_raise(eX509CertError, NULL); if (!X509_print(out, x509)) { BIO_free(out); ossl_raise(eX509CertError, NULL); } str = ossl_membio2str(out); return str; } #if 0 /* * Makes from X509 X509_REQuest */ static VALUE ossl_x509_to_req(VALUE self) { X509 *x509; X509_REQ *req; VALUE obj; GetX509(self, x509); if (!(req = X509_to_X509_REQ(x509, NULL, EVP_md5()))) { ossl_raise(eX509CertError, NULL); } obj = ossl_x509req_new(req); X509_REQ_free(req); return obj; } #endif /* * call-seq: * cert.version => integer */ static VALUE ossl_x509_get_version(VALUE self) { X509 *x509; GetX509(self, x509); return LONG2NUM(X509_get_version(x509)); } /* * call-seq: * cert.version = integer => integer */ static VALUE ossl_x509_set_version(VALUE self, VALUE version) { X509 *x509; long ver; if ((ver = NUM2LONG(version)) < 0) { ossl_raise(eX509CertError, "version must be >= 0!"); } GetX509(self, x509); if (!X509_set_version(x509, ver)) { ossl_raise(eX509CertError, NULL); } return version; } /* * call-seq: * cert.serial => integer */ static VALUE ossl_x509_get_serial(VALUE self) { X509 *x509; GetX509(self, x509); return asn1integer_to_num(X509_get_serialNumber(x509)); } /* * call-seq: * cert.serial = integer => integer */ static VALUE ossl_x509_set_serial(VALUE self, VALUE num) { X509 *x509; GetX509(self, x509); X509_set_serialNumber(x509, num_to_asn1integer(num, X509_get_serialNumber(x509))); return num; } /* * call-seq: * cert.signature_algorithm => string */ static VALUE ossl_x509_get_signature_algorithm(VALUE self) { X509 *x509; BIO *out; VALUE str; GetX509(self, x509); out = BIO_new(BIO_s_mem()); if (!out) ossl_raise(eX509CertError, NULL); if (!i2a_ASN1_OBJECT(out, X509_get0_tbs_sigalg(x509)->algorithm)) { BIO_free(out); ossl_raise(eX509CertError, NULL); } str = ossl_membio2str(out); return str; } /* * call-seq: * cert.subject => name */ static VALUE ossl_x509_get_subject(VALUE self) { X509 *x509; X509_NAME *name; GetX509(self, x509); if (!(name = X509_get_subject_name(x509))) { /* NO DUP - don't free! */ ossl_raise(eX509CertError, NULL); } return ossl_x509name_new(name); } /* * call-seq: * cert.subject = name => name */ static VALUE ossl_x509_set_subject(VALUE self, VALUE subject) { X509 *x509; GetX509(self, x509); if (!X509_set_subject_name(x509, GetX509NamePtr(subject))) { /* DUPs name */ ossl_raise(eX509CertError, NULL); } return subject; } /* * call-seq: * cert.issuer => name */ static VALUE ossl_x509_get_issuer(VALUE self) { X509 *x509; X509_NAME *name; GetX509(self, x509); if(!(name = X509_get_issuer_name(x509))) { /* NO DUP - don't free! */ ossl_raise(eX509CertError, NULL); } return ossl_x509name_new(name); } /* * call-seq: * cert.issuer = name => name */ static VALUE ossl_x509_set_issuer(VALUE self, VALUE issuer) { X509 *x509; GetX509(self, x509); if (!X509_set_issuer_name(x509, GetX509NamePtr(issuer))) { /* DUPs name */ ossl_raise(eX509CertError, NULL); } return issuer; } /* * call-seq: * cert.not_before => time */ static VALUE ossl_x509_get_not_before(VALUE self) { X509 *x509; const ASN1_TIME *asn1time; GetX509(self, x509); if (!(asn1time = X509_get0_notBefore(x509))) { ossl_raise(eX509CertError, NULL); } return asn1time_to_time(asn1time); } /* * call-seq: * cert.not_before = time => time */ static VALUE ossl_x509_set_not_before(VALUE self, VALUE time) { X509 *x509; ASN1_TIME *asn1time; GetX509(self, x509); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_set_notBefore(x509, asn1time)) { ASN1_TIME_free(asn1time); ossl_raise(eX509CertError, "X509_set_notBefore"); } ASN1_TIME_free(asn1time); return time; } /* * call-seq: * cert.not_after => time */ static VALUE ossl_x509_get_not_after(VALUE self) { X509 *x509; const ASN1_TIME *asn1time; GetX509(self, x509); if (!(asn1time = X509_get0_notAfter(x509))) { ossl_raise(eX509CertError, NULL); } return asn1time_to_time(asn1time); } /* * call-seq: * cert.not_after = time => time */ static VALUE ossl_x509_set_not_after(VALUE self, VALUE time) { X509 *x509; ASN1_TIME *asn1time; GetX509(self, x509); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_set_notAfter(x509, asn1time)) { ASN1_TIME_free(asn1time); ossl_raise(eX509CertError, "X509_set_notAfter"); } ASN1_TIME_free(asn1time); return time; } /* * call-seq: * cert.public_key => key */ static VALUE ossl_x509_get_public_key(VALUE self) { X509 *x509; EVP_PKEY *pkey; GetX509(self, x509); if (!(pkey = X509_get_pubkey(x509))) { /* adds an reference */ ossl_raise(eX509CertError, NULL); } return ossl_pkey_new(pkey); /* NO DUP - OK */ } /* * call-seq: * cert.public_key = key */ static VALUE ossl_x509_set_public_key(VALUE self, VALUE key) { X509 *x509; EVP_PKEY *pkey; GetX509(self, x509); pkey = GetPKeyPtr(key); ossl_pkey_check_public_key(pkey); if (!X509_set_pubkey(x509, pkey)) ossl_raise(eX509CertError, "X509_set_pubkey"); return key; } /* * call-seq: * cert.sign(key, digest) => self */ static VALUE ossl_x509_sign(VALUE self, VALUE key, VALUE digest) { X509 *x509; EVP_PKEY *pkey; const EVP_MD *md; pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ md = GetDigestPtr(digest); GetX509(self, x509); if (!X509_sign(x509, pkey, md)) { ossl_raise(eX509CertError, NULL); } return self; } /* * call-seq: * cert.verify(key) => true | false * * Checks that cert signature is made with PRIVversion of this PUBLIC 'key' */ static VALUE ossl_x509_verify(VALUE self, VALUE key) { X509 *x509; EVP_PKEY *pkey; GetX509(self, x509); pkey = GetPKeyPtr(key); ossl_pkey_check_public_key(pkey); switch (X509_verify(x509, pkey)) { case 1: return Qtrue; case 0: ossl_clear_error(); return Qfalse; default: ossl_raise(eX509CertError, NULL); } } /* * call-seq: * cert.check_private_key(key) * * Checks if 'key' is PRIV key for this cert */ static VALUE ossl_x509_check_private_key(VALUE self, VALUE key) { X509 *x509; EVP_PKEY *pkey; /* not needed private key, but should be */ pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ GetX509(self, x509); if (!X509_check_private_key(x509, pkey)) { ossl_clear_error(); return Qfalse; } return Qtrue; } /* * call-seq: * cert.extensions => [extension...] */ static VALUE ossl_x509_get_extensions(VALUE self) { X509 *x509; int count, i; X509_EXTENSION *ext; VALUE ary; GetX509(self, x509); count = X509_get_ext_count(x509); if (count < 0) { return rb_ary_new(); } ary = rb_ary_new2(count); for (i=0; i [ext...] */ static VALUE ossl_x509_set_extensions(VALUE self, VALUE ary) { X509 *x509; X509_EXTENSION *ext; long i; Check_Type(ary, T_ARRAY); /* All ary's members should be X509Extension */ for (i=0; i extension */ static VALUE ossl_x509_add_extension(VALUE self, VALUE extension) { X509 *x509; X509_EXTENSION *ext; GetX509(self, x509); ext = GetX509ExtPtr(extension); if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext - FREE it */ ossl_raise(eX509CertError, NULL); } return extension; } static VALUE ossl_x509_inspect(VALUE self) { return rb_sprintf("#<%"PRIsVALUE": subject=%+"PRIsVALUE", " "issuer=%+"PRIsVALUE", serial=%+"PRIsVALUE", " "not_before=%+"PRIsVALUE", not_after=%+"PRIsVALUE">", rb_obj_class(self), ossl_x509_get_subject(self), ossl_x509_get_issuer(self), ossl_x509_get_serial(self), ossl_x509_get_not_before(self), ossl_x509_get_not_after(self)); } /* * INIT */ void Init_ossl_x509cert(void) { #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); mX509 = rb_define_module_under(mOSSL, "X509"); #endif eX509CertError = rb_define_class_under(mX509, "CertificateError", eOSSLError); /* Document-class: OpenSSL::X509::Certificate * * Implementation of an X.509 certificate as specified in RFC 5280. * Provides access to a certificate's attributes and allows certificates * to be read from a string, but also supports the creation of new * certificates from scratch. * * === Reading a certificate from a file * * Certificate is capable of handling DER-encoded certificates and * certificates encoded in OpenSSL's PEM format. * * raw = File.read "cert.cer" # DER- or PEM-encoded * certificate = OpenSSL::X509::Certificate.new raw * * === Saving a certificate to a file * * A certificate may be encoded in DER format * * cert = ... * File.open("cert.cer", "wb") { |f| f.print cert.to_der } * * or in PEM format * * cert = ... * File.open("cert.pem", "wb") { |f| f.print cert.to_pem } * * X.509 certificates are associated with a private/public key pair, * typically a RSA, DSA or ECC key (see also OpenSSL::PKey::RSA, * OpenSSL::PKey::DSA and OpenSSL::PKey::EC), the public key itself is * stored within the certificate and can be accessed in form of an * OpenSSL::PKey. Certificates are typically used to be able to associate * some form of identity with a key pair, for example web servers serving * pages over HTTPs use certificates to authenticate themselves to the user. * * The public key infrastructure (PKI) model relies on trusted certificate * authorities ("root CAs") that issue these certificates, so that end * users need to base their trust just on a selected few authorities * that themselves again vouch for subordinate CAs issuing their * certificates to end users. * * The OpenSSL::X509 module provides the tools to set up an independent * PKI, similar to scenarios where the 'openssl' command line tool is * used for issuing certificates in a private PKI. * * === Creating a root CA certificate and an end-entity certificate * * First, we need to create a "self-signed" root certificate. To do so, * we need to generate a key first. Please note that the choice of "1" * as a serial number is considered a security flaw for real certificates. * Secure choices are integers in the two-digit byte range and ideally * not sequential but secure random numbers, steps omitted here to keep * the example concise. * * root_key = OpenSSL::PKey::RSA.new 2048 # the CA's public/private key * root_ca = OpenSSL::X509::Certificate.new * root_ca.version = 2 # cf. RFC 5280 - to make it a "v3" certificate * root_ca.serial = 1 * root_ca.subject = OpenSSL::X509::Name.parse "/DC=org/DC=ruby-lang/CN=Ruby CA" * root_ca.issuer = root_ca.subject # root CA's are "self-signed" * root_ca.public_key = root_key.public_key * root_ca.not_before = Time.now * root_ca.not_after = root_ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity * ef = OpenSSL::X509::ExtensionFactory.new * ef.subject_certificate = root_ca * ef.issuer_certificate = root_ca * root_ca.add_extension(ef.create_extension("basicConstraints","CA:TRUE",true)) * root_ca.add_extension(ef.create_extension("keyUsage","keyCertSign, cRLSign", true)) * root_ca.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false)) * root_ca.add_extension(ef.create_extension("authorityKeyIdentifier","keyid:always",false)) * root_ca.sign(root_key, OpenSSL::Digest::SHA256.new) * * The next step is to create the end-entity certificate using the root CA * certificate. * * key = OpenSSL::PKey::RSA.new 2048 * cert = OpenSSL::X509::Certificate.new * cert.version = 2 * cert.serial = 2 * cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=ruby-lang/CN=Ruby certificate" * cert.issuer = root_ca.subject # root CA is the issuer * cert.public_key = key.public_key * cert.not_before = Time.now * cert.not_after = cert.not_before + 1 * 365 * 24 * 60 * 60 # 1 years validity * ef = OpenSSL::X509::ExtensionFactory.new * ef.subject_certificate = cert * ef.issuer_certificate = root_ca * cert.add_extension(ef.create_extension("keyUsage","digitalSignature", true)) * cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false)) * cert.sign(root_key, OpenSSL::Digest::SHA256.new) * */ cX509Cert = rb_define_class_under(mX509, "Certificate", rb_cObject); rb_define_alloc_func(cX509Cert, ossl_x509_alloc); rb_define_method(cX509Cert, "initialize", ossl_x509_initialize, -1); rb_define_copy_func(cX509Cert, ossl_x509_copy); rb_define_method(cX509Cert, "to_der", ossl_x509_to_der, 0); rb_define_method(cX509Cert, "to_pem", ossl_x509_to_pem, 0); rb_define_alias(cX509Cert, "to_s", "to_pem"); rb_define_method(cX509Cert, "to_text", ossl_x509_to_text, 0); rb_define_method(cX509Cert, "version", ossl_x509_get_version, 0); rb_define_method(cX509Cert, "version=", ossl_x509_set_version, 1); rb_define_method(cX509Cert, "signature_algorithm", ossl_x509_get_signature_algorithm, 0); rb_define_method(cX509Cert, "serial", ossl_x509_get_serial, 0); rb_define_method(cX509Cert, "serial=", ossl_x509_set_serial, 1); rb_define_method(cX509Cert, "subject", ossl_x509_get_subject, 0); rb_define_method(cX509Cert, "subject=", ossl_x509_set_subject, 1); rb_define_method(cX509Cert, "issuer", ossl_x509_get_issuer, 0); rb_define_method(cX509Cert, "issuer=", ossl_x509_set_issuer, 1); rb_define_method(cX509Cert, "not_before", ossl_x509_get_not_before, 0); rb_define_method(cX509Cert, "not_before=", ossl_x509_set_not_before, 1); rb_define_method(cX509Cert, "not_after", ossl_x509_get_not_after, 0); rb_define_method(cX509Cert, "not_after=", ossl_x509_set_not_after, 1); rb_define_method(cX509Cert, "public_key", ossl_x509_get_public_key, 0); rb_define_method(cX509Cert, "public_key=", ossl_x509_set_public_key, 1); rb_define_method(cX509Cert, "sign", ossl_x509_sign, 2); rb_define_method(cX509Cert, "verify", ossl_x509_verify, 1); rb_define_method(cX509Cert, "check_private_key", ossl_x509_check_private_key, 1); rb_define_method(cX509Cert, "extensions", ossl_x509_get_extensions, 0); rb_define_method(cX509Cert, "extensions=", ossl_x509_set_extensions, 1); rb_define_method(cX509Cert, "add_extension", ossl_x509_add_extension, 1); rb_define_method(cX509Cert, "inspect", ossl_x509_inspect, 0); } openssl-2.0.9/ext/openssl/ossl_x509crl.c000066400000000000000000000275651336157045000201250ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define NewX509CRL(klass) \ TypedData_Wrap_Struct((klass), &ossl_x509crl_type, 0) #define SetX509CRL(obj, crl) do { \ if (!(crl)) { \ ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (crl); \ } while (0) #define GetX509CRL(obj, crl) do { \ TypedData_Get_Struct((obj), X509_CRL, &ossl_x509crl_type, (crl)); \ if (!(crl)) { \ ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \ } \ } while (0) #define SafeGetX509CRL(obj, crl) do { \ OSSL_Check_Kind((obj), cX509CRL); \ GetX509CRL((obj), (crl)); \ } while (0) /* * Classes */ VALUE cX509CRL; VALUE eX509CRLError; static void ossl_x509crl_free(void *ptr) { X509_CRL_free(ptr); } static const rb_data_type_t ossl_x509crl_type = { "OpenSSL/X509/CRL", { 0, ossl_x509crl_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * PUBLIC */ X509_CRL * GetX509CRLPtr(VALUE obj) { X509_CRL *crl; SafeGetX509CRL(obj, crl); return crl; } X509_CRL * DupX509CRLPtr(VALUE obj) { X509_CRL *crl; SafeGetX509CRL(obj, crl); X509_CRL_up_ref(crl); return crl; } VALUE ossl_x509crl_new(X509_CRL *crl) { X509_CRL *tmp; VALUE obj; obj = NewX509CRL(cX509CRL); tmp = crl ? X509_CRL_dup(crl) : X509_CRL_new(); if(!tmp) ossl_raise(eX509CRLError, NULL); SetX509CRL(obj, tmp); return obj; } /* * PRIVATE */ static VALUE ossl_x509crl_alloc(VALUE klass) { X509_CRL *crl; VALUE obj; obj = NewX509CRL(klass); if (!(crl = X509_CRL_new())) { ossl_raise(eX509CRLError, NULL); } SetX509CRL(obj, crl); return obj; } static VALUE ossl_x509crl_initialize(int argc, VALUE *argv, VALUE self) { BIO *in; X509_CRL *crl, *x = DATA_PTR(self); VALUE arg; if (rb_scan_args(argc, argv, "01", &arg) == 0) { return self; } arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); crl = PEM_read_bio_X509_CRL(in, &x, NULL, NULL); DATA_PTR(self) = x; if (!crl) { OSSL_BIO_reset(in); crl = d2i_X509_CRL_bio(in, &x); DATA_PTR(self) = x; } BIO_free(in); if (!crl) ossl_raise(eX509CRLError, NULL); return self; } static VALUE ossl_x509crl_copy(VALUE self, VALUE other) { X509_CRL *a, *b, *crl; rb_check_frozen(self); if (self == other) return self; GetX509CRL(self, a); SafeGetX509CRL(other, b); if (!(crl = X509_CRL_dup(b))) { ossl_raise(eX509CRLError, NULL); } X509_CRL_free(a); DATA_PTR(self) = crl; return self; } static VALUE ossl_x509crl_get_version(VALUE self) { X509_CRL *crl; long ver; GetX509CRL(self, crl); ver = X509_CRL_get_version(crl); return LONG2NUM(ver); } static VALUE ossl_x509crl_set_version(VALUE self, VALUE version) { X509_CRL *crl; long ver; if ((ver = NUM2LONG(version)) < 0) { ossl_raise(eX509CRLError, "version must be >= 0!"); } GetX509CRL(self, crl); if (!X509_CRL_set_version(crl, ver)) { ossl_raise(eX509CRLError, NULL); } return version; } static VALUE ossl_x509crl_get_signature_algorithm(VALUE self) { X509_CRL *crl; const X509_ALGOR *alg; BIO *out; GetX509CRL(self, crl); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eX509CRLError, NULL); } X509_CRL_get0_signature(crl, NULL, &alg); if (!i2a_ASN1_OBJECT(out, alg->algorithm)) { BIO_free(out); ossl_raise(eX509CRLError, NULL); } return ossl_membio2str(out); } static VALUE ossl_x509crl_get_issuer(VALUE self) { X509_CRL *crl; GetX509CRL(self, crl); return ossl_x509name_new(X509_CRL_get_issuer(crl)); /* NO DUP - don't free */ } static VALUE ossl_x509crl_set_issuer(VALUE self, VALUE issuer) { X509_CRL *crl; GetX509CRL(self, crl); if (!X509_CRL_set_issuer_name(crl, GetX509NamePtr(issuer))) { /* DUPs name */ ossl_raise(eX509CRLError, NULL); } return issuer; } static VALUE ossl_x509crl_get_last_update(VALUE self) { X509_CRL *crl; GetX509CRL(self, crl); return asn1time_to_time(X509_CRL_get0_lastUpdate(crl)); } static VALUE ossl_x509crl_set_last_update(VALUE self, VALUE time) { X509_CRL *crl; ASN1_TIME *asn1time; GetX509CRL(self, crl); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_CRL_set_lastUpdate(crl, asn1time)) { ASN1_TIME_free(asn1time); ossl_raise(eX509CRLError, "X509_CRL_set_lastUpdate"); } ASN1_TIME_free(asn1time); return time; } static VALUE ossl_x509crl_get_next_update(VALUE self) { X509_CRL *crl; GetX509CRL(self, crl); return asn1time_to_time(X509_CRL_get0_nextUpdate(crl)); } static VALUE ossl_x509crl_set_next_update(VALUE self, VALUE time) { X509_CRL *crl; ASN1_TIME *asn1time; GetX509CRL(self, crl); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_CRL_set_nextUpdate(crl, asn1time)) { ASN1_TIME_free(asn1time); ossl_raise(eX509CRLError, "X509_CRL_set_nextUpdate"); } ASN1_TIME_free(asn1time); return time; } static VALUE ossl_x509crl_get_revoked(VALUE self) { X509_CRL *crl; int i, num; X509_REVOKED *rev; VALUE ary, revoked; GetX509CRL(self, crl); num = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); if (num < 0) { OSSL_Debug("num < 0???"); return rb_ary_new(); } ary = rb_ary_new2(num); for(i=0; i * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define NewX509Ext(klass) \ TypedData_Wrap_Struct((klass), &ossl_x509ext_type, 0) #define SetX509Ext(obj, ext) do { \ if (!(ext)) { \ ossl_raise(rb_eRuntimeError, "EXT wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (ext); \ } while (0) #define GetX509Ext(obj, ext) do { \ TypedData_Get_Struct((obj), X509_EXTENSION, &ossl_x509ext_type, (ext)); \ if (!(ext)) { \ ossl_raise(rb_eRuntimeError, "EXT wasn't initialized!"); \ } \ } while (0) #define SafeGetX509Ext(obj, ext) do { \ OSSL_Check_Kind((obj), cX509Ext); \ GetX509Ext((obj), (ext)); \ } while (0) #define MakeX509ExtFactory(klass, obj, ctx) do { \ (obj) = TypedData_Wrap_Struct((klass), &ossl_x509extfactory_type, 0); \ if (!((ctx) = OPENSSL_malloc(sizeof(X509V3_CTX)))) \ ossl_raise(rb_eRuntimeError, "CTX wasn't allocated!"); \ X509V3_set_ctx((ctx), NULL, NULL, NULL, NULL, 0); \ RTYPEDDATA_DATA(obj) = (ctx); \ } while (0) #define GetX509ExtFactory(obj, ctx) do { \ TypedData_Get_Struct((obj), X509V3_CTX, &ossl_x509extfactory_type, (ctx)); \ if (!(ctx)) { \ ossl_raise(rb_eRuntimeError, "CTX wasn't initialized!"); \ } \ } while (0) /* * Classes */ VALUE cX509Ext; VALUE cX509ExtFactory; VALUE eX509ExtError; static void ossl_x509ext_free(void *ptr) { X509_EXTENSION_free(ptr); } static const rb_data_type_t ossl_x509ext_type = { "OpenSSL/X509/EXTENSION", { 0, ossl_x509ext_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * Public */ VALUE ossl_x509ext_new(X509_EXTENSION *ext) { X509_EXTENSION *new; VALUE obj; obj = NewX509Ext(cX509Ext); if (!ext) { new = X509_EXTENSION_new(); } else { new = X509_EXTENSION_dup(ext); } if (!new) { ossl_raise(eX509ExtError, NULL); } SetX509Ext(obj, new); return obj; } X509_EXTENSION * GetX509ExtPtr(VALUE obj) { X509_EXTENSION *ext; SafeGetX509Ext(obj, ext); return ext; } /* * Private */ /* * Ext factory */ static void ossl_x509extfactory_free(void *ctx) { OPENSSL_free(ctx); } static const rb_data_type_t ossl_x509extfactory_type = { "OpenSSL/X509/EXTENSION/Factory", { 0, ossl_x509extfactory_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; static VALUE ossl_x509extfactory_alloc(VALUE klass) { X509V3_CTX *ctx; VALUE obj; MakeX509ExtFactory(klass, obj, ctx); rb_iv_set(obj, "@config", Qnil); return obj; } static VALUE ossl_x509extfactory_set_issuer_cert(VALUE self, VALUE cert) { X509V3_CTX *ctx; GetX509ExtFactory(self, ctx); rb_iv_set(self, "@issuer_certificate", cert); ctx->issuer_cert = GetX509CertPtr(cert); /* NO DUP NEEDED */ return cert; } static VALUE ossl_x509extfactory_set_subject_cert(VALUE self, VALUE cert) { X509V3_CTX *ctx; GetX509ExtFactory(self, ctx); rb_iv_set(self, "@subject_certificate", cert); ctx->subject_cert = GetX509CertPtr(cert); /* NO DUP NEEDED */ return cert; } static VALUE ossl_x509extfactory_set_subject_req(VALUE self, VALUE req) { X509V3_CTX *ctx; GetX509ExtFactory(self, ctx); rb_iv_set(self, "@subject_request", req); ctx->subject_req = GetX509ReqPtr(req); /* NO DUP NEEDED */ return req; } static VALUE ossl_x509extfactory_set_crl(VALUE self, VALUE crl) { X509V3_CTX *ctx; GetX509ExtFactory(self, ctx); rb_iv_set(self, "@crl", crl); ctx->crl = GetX509CRLPtr(crl); /* NO DUP NEEDED */ return crl; } static VALUE ossl_x509extfactory_initialize(int argc, VALUE *argv, VALUE self) { /*X509V3_CTX *ctx;*/ VALUE issuer_cert, subject_cert, subject_req, crl; /*GetX509ExtFactory(self, ctx);*/ rb_scan_args(argc, argv, "04", &issuer_cert, &subject_cert, &subject_req, &crl); if (!NIL_P(issuer_cert)) ossl_x509extfactory_set_issuer_cert(self, issuer_cert); if (!NIL_P(subject_cert)) ossl_x509extfactory_set_subject_cert(self, subject_cert); if (!NIL_P(subject_req)) ossl_x509extfactory_set_subject_req(self, subject_req); if (!NIL_P(crl)) ossl_x509extfactory_set_crl(self, crl); return self; } /* * call-seq: * ef.create_ext(ln_or_sn, "value", critical = false) -> X509::Extension * ef.create_ext(ln_or_sn, "critical,value") -> X509::Extension * * Creates a new X509::Extension with passed values. See also x509v3_config(5). */ static VALUE ossl_x509extfactory_create_ext(int argc, VALUE *argv, VALUE self) { X509V3_CTX *ctx; X509_EXTENSION *ext; VALUE oid, value, critical, valstr, obj; int nid; VALUE rconf; CONF *conf; rb_scan_args(argc, argv, "21", &oid, &value, &critical); StringValueCStr(oid); StringValue(value); if(NIL_P(critical)) critical = Qfalse; nid = OBJ_ln2nid(RSTRING_PTR(oid)); if(!nid) nid = OBJ_sn2nid(RSTRING_PTR(oid)); if(!nid) ossl_raise(eX509ExtError, "unknown OID `%"PRIsVALUE"'", oid); valstr = rb_str_new2(RTEST(critical) ? "critical," : ""); rb_str_append(valstr, value); StringValueCStr(valstr); GetX509ExtFactory(self, ctx); obj = NewX509Ext(cX509Ext); rconf = rb_iv_get(self, "@config"); conf = NIL_P(rconf) ? NULL : DupConfigPtr(rconf); X509V3_set_nconf(ctx, conf); ext = X509V3_EXT_nconf_nid(conf, ctx, nid, RSTRING_PTR(valstr)); X509V3_set_ctx_nodb(ctx); NCONF_free(conf); if (!ext){ ossl_raise(eX509ExtError, "%"PRIsVALUE" = %"PRIsVALUE, oid, valstr); } SetX509Ext(obj, ext); return obj; } /* * Ext */ static VALUE ossl_x509ext_alloc(VALUE klass) { X509_EXTENSION *ext; VALUE obj; obj = NewX509Ext(klass); if(!(ext = X509_EXTENSION_new())){ ossl_raise(eX509ExtError, NULL); } SetX509Ext(obj, ext); return obj; } /* * call-seq: * OpenSSL::X509::Extension.new asn1 * OpenSSL::X509::Extension.new name, value * OpenSSL::X509::Extension.new name, value, critical * * Creates an X509 extension. * * The extension may be created from +asn1+ data or from an extension +name+ * and +value+. The +name+ may be either an OID or an extension name. If * +critical+ is true the extension is marked critical. */ static VALUE ossl_x509ext_initialize(int argc, VALUE *argv, VALUE self) { VALUE oid, value, critical; const unsigned char *p; X509_EXTENSION *ext, *x; GetX509Ext(self, ext); if(rb_scan_args(argc, argv, "12", &oid, &value, &critical) == 1){ oid = ossl_to_der_if_possible(oid); StringValue(oid); p = (unsigned char *)RSTRING_PTR(oid); x = d2i_X509_EXTENSION(&ext, &p, RSTRING_LEN(oid)); DATA_PTR(self) = ext; if(!x) ossl_raise(eX509ExtError, NULL); return self; } rb_funcall(self, rb_intern("oid="), 1, oid); rb_funcall(self, rb_intern("value="), 1, value); if(argc > 2) rb_funcall(self, rb_intern("critical="), 1, critical); return self; } static VALUE ossl_x509ext_initialize_copy(VALUE self, VALUE other) { X509_EXTENSION *ext, *ext_other, *ext_new; rb_check_frozen(self); GetX509Ext(self, ext); SafeGetX509Ext(other, ext_other); ext_new = X509_EXTENSION_dup(ext_other); if (!ext_new) ossl_raise(eX509ExtError, "X509_EXTENSION_dup"); SetX509Ext(self, ext_new); X509_EXTENSION_free(ext); return self; } static VALUE ossl_x509ext_set_oid(VALUE self, VALUE oid) { X509_EXTENSION *ext; ASN1_OBJECT *obj; GetX509Ext(self, ext); obj = OBJ_txt2obj(StringValueCStr(oid), 0); if (!obj) ossl_raise(eX509ExtError, "OBJ_txt2obj"); if (!X509_EXTENSION_set_object(ext, obj)) { ASN1_OBJECT_free(obj); ossl_raise(eX509ExtError, "X509_EXTENSION_set_object"); } ASN1_OBJECT_free(obj); return oid; } static VALUE ossl_x509ext_set_value(VALUE self, VALUE data) { X509_EXTENSION *ext; ASN1_OCTET_STRING *asn1s; GetX509Ext(self, ext); data = ossl_to_der_if_possible(data); StringValue(data); asn1s = X509_EXTENSION_get_data(ext); if (!ASN1_OCTET_STRING_set(asn1s, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data))) { ossl_raise(eX509ExtError, "ASN1_OCTET_STRING_set"); } return data; } static VALUE ossl_x509ext_set_critical(VALUE self, VALUE flag) { X509_EXTENSION *ext; GetX509Ext(self, ext); X509_EXTENSION_set_critical(ext, RTEST(flag) ? 1 : 0); return flag; } static VALUE ossl_x509ext_get_oid(VALUE obj) { X509_EXTENSION *ext; ASN1_OBJECT *extobj; BIO *out; VALUE ret; int nid; GetX509Ext(obj, ext); extobj = X509_EXTENSION_get_object(ext); if ((nid = OBJ_obj2nid(extobj)) != NID_undef) ret = rb_str_new2(OBJ_nid2sn(nid)); else{ if (!(out = BIO_new(BIO_s_mem()))) ossl_raise(eX509ExtError, NULL); i2a_ASN1_OBJECT(out, extobj); ret = ossl_membio2str(out); } return ret; } static VALUE ossl_x509ext_get_value(VALUE obj) { X509_EXTENSION *ext; BIO *out; VALUE ret; GetX509Ext(obj, ext); if (!(out = BIO_new(BIO_s_mem()))) ossl_raise(eX509ExtError, NULL); if (!X509V3_EXT_print(out, ext, 0, 0)) ASN1_STRING_print(out, (ASN1_STRING *)X509_EXTENSION_get_data(ext)); ret = ossl_membio2str(out); return ret; } static VALUE ossl_x509ext_get_critical(VALUE obj) { X509_EXTENSION *ext; GetX509Ext(obj, ext); return X509_EXTENSION_get_critical(ext) ? Qtrue : Qfalse; } static VALUE ossl_x509ext_to_der(VALUE obj) { X509_EXTENSION *ext; unsigned char *p; long len; VALUE str; GetX509Ext(obj, ext); if((len = i2d_X509_EXTENSION(ext, NULL)) <= 0) ossl_raise(eX509ExtError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_X509_EXTENSION(ext, &p) < 0) ossl_raise(eX509ExtError, NULL); ossl_str_adjust(str, p); return str; } /* * INIT */ void Init_ossl_x509ext(void) { #undef rb_intern #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); mX509 = rb_define_module_under(mOSSL, "X509"); #endif eX509ExtError = rb_define_class_under(mX509, "ExtensionError", eOSSLError); cX509ExtFactory = rb_define_class_under(mX509, "ExtensionFactory", rb_cObject); rb_define_alloc_func(cX509ExtFactory, ossl_x509extfactory_alloc); rb_define_method(cX509ExtFactory, "initialize", ossl_x509extfactory_initialize, -1); rb_attr(cX509ExtFactory, rb_intern("issuer_certificate"), 1, 0, Qfalse); rb_attr(cX509ExtFactory, rb_intern("subject_certificate"), 1, 0, Qfalse); rb_attr(cX509ExtFactory, rb_intern("subject_request"), 1, 0, Qfalse); rb_attr(cX509ExtFactory, rb_intern("crl"), 1, 0, Qfalse); rb_attr(cX509ExtFactory, rb_intern("config"), 1, 1, Qfalse); rb_define_method(cX509ExtFactory, "issuer_certificate=", ossl_x509extfactory_set_issuer_cert, 1); rb_define_method(cX509ExtFactory, "subject_certificate=", ossl_x509extfactory_set_subject_cert, 1); rb_define_method(cX509ExtFactory, "subject_request=", ossl_x509extfactory_set_subject_req, 1); rb_define_method(cX509ExtFactory, "crl=", ossl_x509extfactory_set_crl, 1); rb_define_method(cX509ExtFactory, "create_ext", ossl_x509extfactory_create_ext, -1); cX509Ext = rb_define_class_under(mX509, "Extension", rb_cObject); rb_define_alloc_func(cX509Ext, ossl_x509ext_alloc); rb_define_method(cX509Ext, "initialize", ossl_x509ext_initialize, -1); rb_define_copy_func(cX509Ext, ossl_x509ext_initialize_copy); rb_define_method(cX509Ext, "oid=", ossl_x509ext_set_oid, 1); rb_define_method(cX509Ext, "value=", ossl_x509ext_set_value, 1); rb_define_method(cX509Ext, "critical=", ossl_x509ext_set_critical, 1); rb_define_method(cX509Ext, "oid", ossl_x509ext_get_oid, 0); rb_define_method(cX509Ext, "value", ossl_x509ext_get_value, 0); rb_define_method(cX509Ext, "critical?", ossl_x509ext_get_critical, 0); rb_define_method(cX509Ext, "to_der", ossl_x509ext_to_der, 0); } openssl-2.0.9/ext/openssl/ossl_x509name.c000066400000000000000000000322021336157045000202450ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define NewX509Name(klass) \ TypedData_Wrap_Struct((klass), &ossl_x509name_type, 0) #define SetX509Name(obj, name) do { \ if (!(name)) { \ ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \ } \ RTYPEDDATA_DATA(obj) = (name); \ } while (0) #define GetX509Name(obj, name) do { \ TypedData_Get_Struct((obj), X509_NAME, &ossl_x509name_type, (name)); \ if (!(name)) { \ ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \ } \ } while (0) #define SafeGetX509Name(obj, name) do { \ OSSL_Check_Kind((obj), cX509Name); \ GetX509Name((obj), (name)); \ } while (0) #define OBJECT_TYPE_TEMPLATE \ rb_const_get(cX509Name, rb_intern("OBJECT_TYPE_TEMPLATE")) #define DEFAULT_OBJECT_TYPE \ rb_const_get(cX509Name, rb_intern("DEFAULT_OBJECT_TYPE")) /* * Classes */ VALUE cX509Name; VALUE eX509NameError; static void ossl_x509name_free(void *ptr) { X509_NAME_free(ptr); } static const rb_data_type_t ossl_x509name_type = { "OpenSSL/X509/NAME", { 0, ossl_x509name_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * Public */ VALUE ossl_x509name_new(X509_NAME *name) { X509_NAME *new; VALUE obj; obj = NewX509Name(cX509Name); if (!name) { new = X509_NAME_new(); } else { new = X509_NAME_dup(name); } if (!new) { ossl_raise(eX509NameError, NULL); } SetX509Name(obj, new); return obj; } X509_NAME * GetX509NamePtr(VALUE obj) { X509_NAME *name; SafeGetX509Name(obj, name); return name; } /* * Private */ static VALUE ossl_x509name_alloc(VALUE klass) { X509_NAME *name; VALUE obj; obj = NewX509Name(klass); if (!(name = X509_NAME_new())) { ossl_raise(eX509NameError, NULL); } SetX509Name(obj, name); return obj; } static ID id_aref; static VALUE ossl_x509name_add_entry(int, VALUE*, VALUE); #define rb_aref(obj, key) rb_funcall((obj), id_aref, 1, (key)) static VALUE ossl_x509name_init_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, args)) { VALUE self = rb_ary_entry(args, 0); VALUE template = rb_ary_entry(args, 1); VALUE entry[3]; Check_Type(i, T_ARRAY); entry[0] = rb_ary_entry(i, 0); entry[1] = rb_ary_entry(i, 1); entry[2] = rb_ary_entry(i, 2); if(NIL_P(entry[2])) entry[2] = rb_aref(template, entry[0]); if(NIL_P(entry[2])) entry[2] = DEFAULT_OBJECT_TYPE; ossl_x509name_add_entry(3, entry, self); return Qnil; } /* * call-seq: * X509::Name.new => name * X509::Name.new(der) => name * X509::Name.new(distinguished_name) => name * X509::Name.new(distinguished_name, template) => name * * Creates a new Name. * * A name may be created from a DER encoded string +der+, an Array * representing a +distinguished_name+ or a +distinguished_name+ along with a * +template+. * * name = OpenSSL::X509::Name.new [['CN', 'nobody'], ['DC', 'example']] * * name = OpenSSL::X509::Name.new name.to_der * * See add_entry for a description of the +distinguished_name+ Array's * contents */ static VALUE ossl_x509name_initialize(int argc, VALUE *argv, VALUE self) { X509_NAME *name; VALUE arg, template; GetX509Name(self, name); if (rb_scan_args(argc, argv, "02", &arg, &template) == 0) { return self; } else { VALUE tmp = rb_check_array_type(arg); if (!NIL_P(tmp)) { VALUE args; if(NIL_P(template)) template = OBJECT_TYPE_TEMPLATE; args = rb_ary_new3(2, self, template); rb_block_call(tmp, rb_intern("each"), 0, 0, ossl_x509name_init_i, args); } else{ const unsigned char *p; VALUE str = ossl_to_der_if_possible(arg); X509_NAME *x; StringValue(str); p = (unsigned char *)RSTRING_PTR(str); x = d2i_X509_NAME(&name, &p, RSTRING_LEN(str)); DATA_PTR(self) = name; if(!x){ ossl_raise(eX509NameError, NULL); } } } return self; } static VALUE ossl_x509name_initialize_copy(VALUE self, VALUE other) { X509_NAME *name, *name_other, *name_new; rb_check_frozen(self); GetX509Name(self, name); SafeGetX509Name(other, name_other); name_new = X509_NAME_dup(name_other); if (!name_new) ossl_raise(eX509NameError, "X509_NAME_dup"); SetX509Name(self, name_new); X509_NAME_free(name); return self; } /* * call-seq: * name.add_entry(oid, value [, type]) => self * * Adds a new entry with the given +oid+ and +value+ to this name. The +oid+ * is an object identifier defined in ASN.1. Some common OIDs are: * * C:: Country Name * CN:: Common Name * DC:: Domain Component * O:: Organization Name * OU:: Organizational Unit Name * ST:: State or Province Name */ static VALUE ossl_x509name_add_entry(int argc, VALUE *argv, VALUE self) { X509_NAME *name; VALUE oid, value, type; const char *oid_name; rb_scan_args(argc, argv, "21", &oid, &value, &type); oid_name = StringValueCStr(oid); StringValue(value); if(NIL_P(type)) type = rb_aref(OBJECT_TYPE_TEMPLATE, oid); GetX509Name(self, name); if (!X509_NAME_add_entry_by_txt(name, oid_name, NUM2INT(type), (const unsigned char *)RSTRING_PTR(value), RSTRING_LENINT(value), -1, 0)) { ossl_raise(eX509NameError, NULL); } return self; } static VALUE ossl_x509name_to_s_old(VALUE self) { X509_NAME *name; char *buf; GetX509Name(self, name); buf = X509_NAME_oneline(name, NULL, 0); if (!buf) ossl_raise(eX509NameError, "X509_NAME_oneline"); return ossl_buf2str(buf, rb_long2int(strlen(buf))); } static VALUE x509name_print(VALUE self, unsigned long iflag) { X509_NAME *name; BIO *out; int ret; GetX509Name(self, name); out = BIO_new(BIO_s_mem()); if (!out) ossl_raise(eX509NameError, NULL); ret = X509_NAME_print_ex(out, name, 0, iflag); if (ret < 0 || iflag == XN_FLAG_COMPAT && ret == 0) { BIO_free(out); ossl_raise(eX509NameError, "X509_NAME_print_ex"); } return ossl_membio2str(out); } /* * call-seq: * name.to_s => string * name.to_s(flags) => string * * Returns this name as a Distinguished Name string. +flags+ may be one of: * * * OpenSSL::X509::Name::COMPAT * * OpenSSL::X509::Name::RFC2253 * * OpenSSL::X509::Name::ONELINE * * OpenSSL::X509::Name::MULTILINE */ static VALUE ossl_x509name_to_s(int argc, VALUE *argv, VALUE self) { rb_check_arity(argc, 0, 1); /* name.to_s(nil) was allowed */ if (!argc || NIL_P(argv[0])) return ossl_x509name_to_s_old(self); else return x509name_print(self, NUM2ULONG(argv[0])); } /* * call-seq: * name.to_a => [[name, data, type], ...] * * Returns an Array representation of the distinguished name suitable for * passing to ::new */ static VALUE ossl_x509name_to_a(VALUE self) { X509_NAME *name; X509_NAME_ENTRY *entry; int i,entries,nid; char long_name[512]; const char *short_name; VALUE ary, vname, ret; ASN1_STRING *value; GetX509Name(self, name); entries = X509_NAME_entry_count(name); if (entries < 0) { OSSL_Debug("name entries < 0!"); return rb_ary_new(); } ret = rb_ary_new2(entries); for (i=0; itype)); rb_ary_push(ret, ary); } return ret; } static int ossl_x509name_cmp0(VALUE self, VALUE other) { X509_NAME *name1, *name2; GetX509Name(self, name1); SafeGetX509Name(other, name2); return X509_NAME_cmp(name1, name2); } /* * call-seq: * name.cmp other => integer * name.<=> other => integer * * Compares this Name with +other+ and returns 0 if they are the same and -1 or * +1 if they are greater or less than each other respectively. */ static VALUE ossl_x509name_cmp(VALUE self, VALUE other) { int result; result = ossl_x509name_cmp0(self, other); if (result < 0) return INT2FIX(-1); if (result > 0) return INT2FIX(1); return INT2FIX(0); } /* * call-seq: * name.eql? other => boolean * * Returns true if +name+ and +other+ refer to the same hash key. */ static VALUE ossl_x509name_eql(VALUE self, VALUE other) { if (!rb_obj_is_kind_of(other, cX509Name)) return Qfalse; return ossl_x509name_cmp0(self, other) == 0 ? Qtrue : Qfalse; } /* * call-seq: * name.hash => integer * * The hash value returned is suitable for use as a certificate's filename in * a CA path. */ static VALUE ossl_x509name_hash(VALUE self) { X509_NAME *name; unsigned long hash; GetX509Name(self, name); hash = X509_NAME_hash(name); return ULONG2NUM(hash); } #ifdef HAVE_X509_NAME_HASH_OLD /* * call-seq: * name.hash_old => integer * * Returns an MD5 based hash used in OpenSSL 0.9.X. */ static VALUE ossl_x509name_hash_old(VALUE self) { X509_NAME *name; unsigned long hash; GetX509Name(self, name); hash = X509_NAME_hash_old(name); return ULONG2NUM(hash); } #endif /* * call-seq: * name.to_der => string * * Converts the name to DER encoding */ static VALUE ossl_x509name_to_der(VALUE self) { X509_NAME *name; VALUE str; long len; unsigned char *p; GetX509Name(self, name); if((len = i2d_X509_NAME(name, NULL)) <= 0) ossl_raise(eX509NameError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if(i2d_X509_NAME(name, &p) <= 0) ossl_raise(eX509NameError, NULL); ossl_str_adjust(str, p); return str; } /* * Document-class: OpenSSL::X509::Name * * An X.509 name represents a hostname, email address or other entity * associated with a public key. * * You can create a Name by parsing a distinguished name String or by * supplying the distinguished name as an Array. * * name = OpenSSL::X509::Name.parse 'CN=nobody/DC=example' * * name = OpenSSL::X509::Name.new [['CN', 'nobody'], ['DC', 'example']] */ void Init_ossl_x509name(void) { #undef rb_intern VALUE utf8str, ptrstr, ia5str, hash; #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); mX509 = rb_define_module_under(mOSSL, "X509"); #endif id_aref = rb_intern("[]"); eX509NameError = rb_define_class_under(mX509, "NameError", eOSSLError); cX509Name = rb_define_class_under(mX509, "Name", rb_cObject); rb_include_module(cX509Name, rb_mComparable); rb_define_alloc_func(cX509Name, ossl_x509name_alloc); rb_define_method(cX509Name, "initialize", ossl_x509name_initialize, -1); rb_define_copy_func(cX509Name, ossl_x509name_initialize_copy); rb_define_method(cX509Name, "add_entry", ossl_x509name_add_entry, -1); rb_define_method(cX509Name, "to_s", ossl_x509name_to_s, -1); rb_define_method(cX509Name, "to_a", ossl_x509name_to_a, 0); rb_define_method(cX509Name, "cmp", ossl_x509name_cmp, 1); rb_define_alias(cX509Name, "<=>", "cmp"); rb_define_method(cX509Name, "eql?", ossl_x509name_eql, 1); rb_define_method(cX509Name, "hash", ossl_x509name_hash, 0); #ifdef HAVE_X509_NAME_HASH_OLD rb_define_method(cX509Name, "hash_old", ossl_x509name_hash_old, 0); #endif rb_define_method(cX509Name, "to_der", ossl_x509name_to_der, 0); utf8str = INT2NUM(V_ASN1_UTF8STRING); ptrstr = INT2NUM(V_ASN1_PRINTABLESTRING); ia5str = INT2NUM(V_ASN1_IA5STRING); /* * The default object type for name entries. */ rb_define_const(cX509Name, "DEFAULT_OBJECT_TYPE", utf8str); hash = rb_hash_new(); RHASH_SET_IFNONE(hash, utf8str); rb_hash_aset(hash, rb_str_new2("C"), ptrstr); rb_hash_aset(hash, rb_str_new2("countryName"), ptrstr); rb_hash_aset(hash, rb_str_new2("serialNumber"), ptrstr); rb_hash_aset(hash, rb_str_new2("dnQualifier"), ptrstr); rb_hash_aset(hash, rb_str_new2("DC"), ia5str); rb_hash_aset(hash, rb_str_new2("domainComponent"), ia5str); rb_hash_aset(hash, rb_str_new2("emailAddress"), ia5str); /* * The default object type template for name entries. */ rb_define_const(cX509Name, "OBJECT_TYPE_TEMPLATE", hash); /* * A flag for #to_s. * * Breaks the name returned into multiple lines if longer than 80 * characters. */ rb_define_const(cX509Name, "COMPAT", ULONG2NUM(XN_FLAG_COMPAT)); /* * A flag for #to_s. * * Returns an RFC2253 format name. */ rb_define_const(cX509Name, "RFC2253", ULONG2NUM(XN_FLAG_RFC2253)); /* * A flag for #to_s. * * Returns a more readable format than RFC2253. */ rb_define_const(cX509Name, "ONELINE", ULONG2NUM(XN_FLAG_ONELINE)); /* * A flag for #to_s. * * Returns a multiline format. */ rb_define_const(cX509Name, "MULTILINE", ULONG2NUM(XN_FLAG_MULTILINE)); } openssl-2.0.9/ext/openssl/ossl_x509req.c000066400000000000000000000236631336157045000201270ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define NewX509Req(klass) \ TypedData_Wrap_Struct((klass), &ossl_x509req_type, 0) #define SetX509Req(obj, req) do { \ if (!(req)) { \ ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (req); \ } while (0) #define GetX509Req(obj, req) do { \ TypedData_Get_Struct((obj), X509_REQ, &ossl_x509req_type, (req)); \ if (!(req)) { \ ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \ } \ } while (0) #define SafeGetX509Req(obj, req) do { \ OSSL_Check_Kind((obj), cX509Req); \ GetX509Req((obj), (req)); \ } while (0) /* * Classes */ VALUE cX509Req; VALUE eX509ReqError; static void ossl_x509req_free(void *ptr) { X509_REQ_free(ptr); } static const rb_data_type_t ossl_x509req_type = { "OpenSSL/X509/REQ", { 0, ossl_x509req_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * Public functions */ VALUE ossl_x509req_new(X509_REQ *req) { X509_REQ *new; VALUE obj; obj = NewX509Req(cX509Req); if (!req) { new = X509_REQ_new(); } else { new = X509_REQ_dup(req); } if (!new) { ossl_raise(eX509ReqError, NULL); } SetX509Req(obj, new); return obj; } X509_REQ * GetX509ReqPtr(VALUE obj) { X509_REQ *req; SafeGetX509Req(obj, req); return req; } X509_REQ * DupX509ReqPtr(VALUE obj) { X509_REQ *req, *new; SafeGetX509Req(obj, req); if (!(new = X509_REQ_dup(req))) { ossl_raise(eX509ReqError, NULL); } return new; } /* * Private functions */ static VALUE ossl_x509req_alloc(VALUE klass) { X509_REQ *req; VALUE obj; obj = NewX509Req(klass); if (!(req = X509_REQ_new())) { ossl_raise(eX509ReqError, NULL); } SetX509Req(obj, req); return obj; } static VALUE ossl_x509req_initialize(int argc, VALUE *argv, VALUE self) { BIO *in; X509_REQ *req, *x = DATA_PTR(self); VALUE arg; if (rb_scan_args(argc, argv, "01", &arg) == 0) { return self; } arg = ossl_to_der_if_possible(arg); in = ossl_obj2bio(&arg); req = PEM_read_bio_X509_REQ(in, &x, NULL, NULL); DATA_PTR(self) = x; if (!req) { OSSL_BIO_reset(in); req = d2i_X509_REQ_bio(in, &x); DATA_PTR(self) = x; } BIO_free(in); if (!req) ossl_raise(eX509ReqError, NULL); return self; } static VALUE ossl_x509req_copy(VALUE self, VALUE other) { X509_REQ *a, *b, *req; rb_check_frozen(self); if (self == other) return self; GetX509Req(self, a); SafeGetX509Req(other, b); if (!(req = X509_REQ_dup(b))) { ossl_raise(eX509ReqError, NULL); } X509_REQ_free(a); DATA_PTR(self) = req; return self; } static VALUE ossl_x509req_to_pem(VALUE self) { X509_REQ *req; BIO *out; GetX509Req(self, req); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eX509ReqError, NULL); } if (!PEM_write_bio_X509_REQ(out, req)) { BIO_free(out); ossl_raise(eX509ReqError, NULL); } return ossl_membio2str(out); } static VALUE ossl_x509req_to_der(VALUE self) { X509_REQ *req; VALUE str; long len; unsigned char *p; GetX509Req(self, req); if ((len = i2d_X509_REQ(req, NULL)) <= 0) ossl_raise(eX509ReqError, NULL); str = rb_str_new(0, len); p = (unsigned char *)RSTRING_PTR(str); if (i2d_X509_REQ(req, &p) <= 0) ossl_raise(eX509ReqError, NULL); ossl_str_adjust(str, p); return str; } static VALUE ossl_x509req_to_text(VALUE self) { X509_REQ *req; BIO *out; GetX509Req(self, req); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eX509ReqError, NULL); } if (!X509_REQ_print(out, req)) { BIO_free(out); ossl_raise(eX509ReqError, NULL); } return ossl_membio2str(out); } #if 0 /* * Makes X509 from X509_REQuest */ static VALUE ossl_x509req_to_x509(VALUE self, VALUE days, VALUE key) { X509_REQ *req; X509 *x509; GetX509Req(self, req); ... if (!(x509 = X509_REQ_to_X509(req, d, pkey))) { ossl_raise(eX509ReqError, NULL); } return ossl_x509_new(x509); } #endif static VALUE ossl_x509req_get_version(VALUE self) { X509_REQ *req; long version; GetX509Req(self, req); version = X509_REQ_get_version(req); return LONG2NUM(version); } static VALUE ossl_x509req_set_version(VALUE self, VALUE version) { X509_REQ *req; long ver; if ((ver = NUM2LONG(version)) < 0) { ossl_raise(eX509ReqError, "version must be >= 0!"); } GetX509Req(self, req); if (!X509_REQ_set_version(req, ver)) { ossl_raise(eX509ReqError, "X509_REQ_set_version"); } return version; } static VALUE ossl_x509req_get_subject(VALUE self) { X509_REQ *req; X509_NAME *name; GetX509Req(self, req); if (!(name = X509_REQ_get_subject_name(req))) { /* NO DUP - don't free */ ossl_raise(eX509ReqError, NULL); } return ossl_x509name_new(name); } static VALUE ossl_x509req_set_subject(VALUE self, VALUE subject) { X509_REQ *req; GetX509Req(self, req); /* DUPs name */ if (!X509_REQ_set_subject_name(req, GetX509NamePtr(subject))) { ossl_raise(eX509ReqError, NULL); } return subject; } static VALUE ossl_x509req_get_signature_algorithm(VALUE self) { X509_REQ *req; const X509_ALGOR *alg; BIO *out; GetX509Req(self, req); if (!(out = BIO_new(BIO_s_mem()))) { ossl_raise(eX509ReqError, NULL); } X509_REQ_get0_signature(req, NULL, &alg); if (!i2a_ASN1_OBJECT(out, alg->algorithm)) { BIO_free(out); ossl_raise(eX509ReqError, NULL); } return ossl_membio2str(out); } static VALUE ossl_x509req_get_public_key(VALUE self) { X509_REQ *req; EVP_PKEY *pkey; GetX509Req(self, req); if (!(pkey = X509_REQ_get_pubkey(req))) { /* adds reference */ ossl_raise(eX509ReqError, NULL); } return ossl_pkey_new(pkey); /* NO DUP - OK */ } static VALUE ossl_x509req_set_public_key(VALUE self, VALUE key) { X509_REQ *req; EVP_PKEY *pkey; GetX509Req(self, req); pkey = GetPKeyPtr(key); ossl_pkey_check_public_key(pkey); if (!X509_REQ_set_pubkey(req, pkey)) ossl_raise(eX509ReqError, "X509_REQ_set_pubkey"); return key; } static VALUE ossl_x509req_sign(VALUE self, VALUE key, VALUE digest) { X509_REQ *req; EVP_PKEY *pkey; const EVP_MD *md; GetX509Req(self, req); pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */ md = GetDigestPtr(digest); if (!X509_REQ_sign(req, pkey, md)) { ossl_raise(eX509ReqError, NULL); } return self; } /* * Checks that cert signature is made with PRIVversion of this PUBLIC 'key' */ static VALUE ossl_x509req_verify(VALUE self, VALUE key) { X509_REQ *req; EVP_PKEY *pkey; GetX509Req(self, req); pkey = GetPKeyPtr(key); ossl_pkey_check_public_key(pkey); switch (X509_REQ_verify(req, pkey)) { case 1: return Qtrue; case 0: ossl_clear_error(); return Qfalse; default: ossl_raise(eX509ReqError, NULL); } } static VALUE ossl_x509req_get_attributes(VALUE self) { X509_REQ *req; int count, i; X509_ATTRIBUTE *attr; VALUE ary; GetX509Req(self, req); count = X509_REQ_get_attr_count(req); if (count < 0) { OSSL_Debug("count < 0???"); return rb_ary_new(); } ary = rb_ary_new2(count); for (i=0; i * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define NewX509Rev(klass) \ TypedData_Wrap_Struct((klass), &ossl_x509rev_type, 0) #define SetX509Rev(obj, rev) do { \ if (!(rev)) { \ ossl_raise(rb_eRuntimeError, "REV wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (rev); \ } while (0) #define GetX509Rev(obj, rev) do { \ TypedData_Get_Struct((obj), X509_REVOKED, &ossl_x509rev_type, (rev)); \ if (!(rev)) { \ ossl_raise(rb_eRuntimeError, "REV wasn't initialized!"); \ } \ } while (0) #define SafeGetX509Rev(obj, rev) do { \ OSSL_Check_Kind((obj), cX509Rev); \ GetX509Rev((obj), (rev)); \ } while (0) /* * Classes */ VALUE cX509Rev; VALUE eX509RevError; static void ossl_x509rev_free(void *ptr) { X509_REVOKED_free(ptr); } static const rb_data_type_t ossl_x509rev_type = { "OpenSSL/X509/REV", { 0, ossl_x509rev_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * PUBLIC */ VALUE ossl_x509revoked_new(X509_REVOKED *rev) { X509_REVOKED *new; VALUE obj; obj = NewX509Rev(cX509Rev); if (!rev) { new = X509_REVOKED_new(); } else { new = X509_REVOKED_dup(rev); } if (!new) { ossl_raise(eX509RevError, NULL); } SetX509Rev(obj, new); return obj; } X509_REVOKED * DupX509RevokedPtr(VALUE obj) { X509_REVOKED *rev, *new; SafeGetX509Rev(obj, rev); if (!(new = X509_REVOKED_dup(rev))) { ossl_raise(eX509RevError, NULL); } return new; } /* * PRIVATE */ static VALUE ossl_x509revoked_alloc(VALUE klass) { X509_REVOKED *rev; VALUE obj; obj = NewX509Rev(klass); if (!(rev = X509_REVOKED_new())) { ossl_raise(eX509RevError, NULL); } SetX509Rev(obj, rev); return obj; } static VALUE ossl_x509revoked_initialize(int argc, VALUE *argv, VALUE self) { /* EMPTY */ return self; } static VALUE ossl_x509revoked_initialize_copy(VALUE self, VALUE other) { X509_REVOKED *rev, *rev_other, *rev_new; rb_check_frozen(self); GetX509Rev(self, rev); SafeGetX509Rev(other, rev_other); rev_new = X509_REVOKED_dup(rev_other); if (!rev_new) ossl_raise(eX509RevError, "X509_REVOKED_dup"); SetX509Rev(self, rev_new); X509_REVOKED_free(rev); return self; } static VALUE ossl_x509revoked_get_serial(VALUE self) { X509_REVOKED *rev; GetX509Rev(self, rev); return asn1integer_to_num(X509_REVOKED_get0_serialNumber(rev)); } static VALUE ossl_x509revoked_set_serial(VALUE self, VALUE num) { X509_REVOKED *rev; ASN1_INTEGER *asn1int; GetX509Rev(self, rev); asn1int = num_to_asn1integer(num, NULL); if (!X509_REVOKED_set_serialNumber(rev, asn1int)) { ASN1_INTEGER_free(asn1int); ossl_raise(eX509RevError, "X509_REVOKED_set_serialNumber"); } ASN1_INTEGER_free(asn1int); return num; } static VALUE ossl_x509revoked_get_time(VALUE self) { X509_REVOKED *rev; GetX509Rev(self, rev); return asn1time_to_time(X509_REVOKED_get0_revocationDate(rev)); } static VALUE ossl_x509revoked_set_time(VALUE self, VALUE time) { X509_REVOKED *rev; ASN1_TIME *asn1time; GetX509Rev(self, rev); asn1time = ossl_x509_time_adjust(NULL, time); if (!X509_REVOKED_set_revocationDate(rev, asn1time)) { ASN1_TIME_free(asn1time); ossl_raise(eX509RevError, "X509_REVOKED_set_revocationDate"); } ASN1_TIME_free(asn1time); return time; } /* * Gets X509v3 extensions as array of X509Ext objects */ static VALUE ossl_x509revoked_get_extensions(VALUE self) { X509_REVOKED *rev; int count, i; X509_EXTENSION *ext; VALUE ary; GetX509Rev(self, rev); count = X509_REVOKED_get_ext_count(rev); if (count < 0) { OSSL_Debug("count < 0???"); return rb_ary_new(); } ary = rb_ary_new2(count); for (i=0; i * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #include "ossl.h" #define NewX509Store(klass) \ TypedData_Wrap_Struct((klass), &ossl_x509store_type, 0) #define SetX509Store(obj, st) do { \ if (!(st)) { \ ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (st); \ } while (0) #define GetX509Store(obj, st) do { \ TypedData_Get_Struct((obj), X509_STORE, &ossl_x509store_type, (st)); \ if (!(st)) { \ ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \ } \ } while (0) #define SafeGetX509Store(obj, st) do { \ OSSL_Check_Kind((obj), cX509Store); \ GetX509Store((obj), (st)); \ } while (0) #define NewX509StCtx(klass) \ TypedData_Wrap_Struct((klass), &ossl_x509stctx_type, 0) #define SetX509StCtx(obj, ctx) do { \ if (!(ctx)) { \ ossl_raise(rb_eRuntimeError, "STORE_CTX wasn't initialized!"); \ } \ RTYPEDDATA_DATA(obj) = (ctx); \ } while (0) #define GetX509StCtx(obj, ctx) do { \ TypedData_Get_Struct((obj), X509_STORE_CTX, &ossl_x509stctx_type, (ctx)); \ if (!(ctx)) { \ ossl_raise(rb_eRuntimeError, "STORE_CTX is out of scope!"); \ } \ } while (0) #define SafeGetX509StCtx(obj, storep) do { \ OSSL_Check_Kind((obj), cX509StoreContext); \ GetX509Store((obj), (ctx)); \ } while (0) /* * Verify callback stuff */ static int stctx_ex_verify_cb_idx, store_ex_verify_cb_idx; static VALUE ossl_x509stctx_new(X509_STORE_CTX *); struct ossl_verify_cb_args { VALUE proc; VALUE preverify_ok; VALUE store_ctx; }; static VALUE call_verify_cb_proc(struct ossl_verify_cb_args *args) { return rb_funcall(args->proc, rb_intern("call"), 2, args->preverify_ok, args->store_ctx); } int ossl_verify_cb_call(VALUE proc, int ok, X509_STORE_CTX *ctx) { VALUE rctx, ret; struct ossl_verify_cb_args args; int state; if (NIL_P(proc)) return ok; ret = Qfalse; rctx = rb_protect((VALUE(*)(VALUE))ossl_x509stctx_new, (VALUE)ctx, &state); if (state) { rb_set_errinfo(Qnil); rb_warn("StoreContext initialization failure"); } else { args.proc = proc; args.preverify_ok = ok ? Qtrue : Qfalse; args.store_ctx = rctx; ret = rb_protect((VALUE(*)(VALUE))call_verify_cb_proc, (VALUE)&args, &state); if (state) { rb_set_errinfo(Qnil); rb_warn("exception in verify_callback is ignored"); } RTYPEDDATA_DATA(rctx) = NULL; } if (ret == Qtrue) { X509_STORE_CTX_set_error(ctx, X509_V_OK); ok = 1; } else { if (X509_STORE_CTX_get_error(ctx) == X509_V_OK) X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED); ok = 0; } return ok; } /* * Classes */ VALUE cX509Store; VALUE cX509StoreContext; VALUE eX509StoreError; static void ossl_x509store_free(void *ptr) { X509_STORE_free(ptr); } static const rb_data_type_t ossl_x509store_type = { "OpenSSL/X509/STORE", { 0, ossl_x509store_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * Public functions */ VALUE ossl_x509store_new(X509_STORE *store) { VALUE obj; obj = NewX509Store(cX509Store); SetX509Store(obj, store); return obj; } X509_STORE * GetX509StorePtr(VALUE obj) { X509_STORE *store; SafeGetX509Store(obj, store); return store; } X509_STORE * DupX509StorePtr(VALUE obj) { X509_STORE *store; SafeGetX509Store(obj, store); X509_STORE_up_ref(store); return store; } /* * Private functions */ static int x509store_verify_cb(int ok, X509_STORE_CTX *ctx) { VALUE proc; proc = (VALUE)X509_STORE_CTX_get_ex_data(ctx, stctx_ex_verify_cb_idx); if (!proc) proc = (VALUE)X509_STORE_get_ex_data(X509_STORE_CTX_get0_store(ctx), store_ex_verify_cb_idx); if (!proc) return ok; return ossl_verify_cb_call(proc, ok, ctx); } static VALUE ossl_x509store_alloc(VALUE klass) { X509_STORE *store; VALUE obj; obj = NewX509Store(klass); if((store = X509_STORE_new()) == NULL){ ossl_raise(eX509StoreError, NULL); } SetX509Store(obj, store); return obj; } /* * General callback for OpenSSL verify */ static VALUE ossl_x509store_set_vfy_cb(VALUE self, VALUE cb) { X509_STORE *store; GetX509Store(self, store); X509_STORE_set_ex_data(store, store_ex_verify_cb_idx, (void *)cb); rb_iv_set(self, "@verify_callback", cb); return cb; } /* * call-seq: * X509::Store.new => store * * Creates a new X509::Store. */ static VALUE ossl_x509store_initialize(int argc, VALUE *argv, VALUE self) { X509_STORE *store; /* BUG: This method takes any number of arguments but appears to ignore them. */ GetX509Store(self, store); #if !defined(HAVE_OPAQUE_OPENSSL) /* [Bug #405] [Bug #1678] [Bug #3000]; already fixed? */ store->ex_data.sk = NULL; #endif X509_STORE_set_verify_cb(store, x509store_verify_cb); ossl_x509store_set_vfy_cb(self, Qnil); /* last verification status */ rb_iv_set(self, "@error", Qnil); rb_iv_set(self, "@error_string", Qnil); rb_iv_set(self, "@chain", Qnil); rb_iv_set(self, "@time", Qnil); return self; } /* * call-seq: * store.flags = flag * * Sets +flag+ to the Store. +flag+ consists of zero or more of the constants * defined in with name V_FLAG_* or'ed together. */ static VALUE ossl_x509store_set_flags(VALUE self, VALUE flags) { X509_STORE *store; long f = NUM2LONG(flags); GetX509Store(self, store); X509_STORE_set_flags(store, f); return flags; } /* * call-seq: * store.purpose = purpose * * Sets the store's purpose to +purpose+. If specified, the verifications on * the store will check every untrusted certificate's extensions are consistent * with the purpose. The purpose is specified by constants: * * * X509::PURPOSE_SSL_CLIENT * * X509::PURPOSE_SSL_SERVER * * X509::PURPOSE_NS_SSL_SERVER * * X509::PURPOSE_SMIME_SIGN * * X509::PURPOSE_SMIME_ENCRYPT * * X509::PURPOSE_CRL_SIGN * * X509::PURPOSE_ANY * * X509::PURPOSE_OCSP_HELPER * * X509::PURPOSE_TIMESTAMP_SIGN */ static VALUE ossl_x509store_set_purpose(VALUE self, VALUE purpose) { X509_STORE *store; int p = NUM2INT(purpose); GetX509Store(self, store); X509_STORE_set_purpose(store, p); return purpose; } /* * call-seq: * store.trust = trust */ static VALUE ossl_x509store_set_trust(VALUE self, VALUE trust) { X509_STORE *store; int t = NUM2INT(trust); GetX509Store(self, store); X509_STORE_set_trust(store, t); return trust; } /* * call-seq: * store.time = time * * Sets the time to be used in verifications. */ static VALUE ossl_x509store_set_time(VALUE self, VALUE time) { rb_iv_set(self, "@time", time); return time; } /* * call-seq: * store.add_file(file) -> self * * Adds the certificates in +file+ to the certificate store. The +file+ can * contain multiple PEM-encoded certificates. */ static VALUE ossl_x509store_add_file(VALUE self, VALUE file) { X509_STORE *store; X509_LOOKUP *lookup; char *path = NULL; if(file != Qnil){ rb_check_safe_obj(file); path = StringValueCStr(file); } GetX509Store(self, store); lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); if(lookup == NULL) ossl_raise(eX509StoreError, NULL); if(X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM) != 1){ ossl_raise(eX509StoreError, NULL); } #if OPENSSL_VERSION_NUMBER < 0x10101000 || defined(LIBRESSL_VERSION_NUMBER) /* * X509_load_cert_crl_file() which is called from X509_LOOKUP_load_file() * did not check the return value of X509_STORE_add_{cert,crl}(), leaking * "cert already in hash table" errors on the error queue, if duplicate * certificates are found. This will be fixed by OpenSSL 1.1.1. */ ossl_clear_error(); #endif return self; } /* * call-seq: * store.add_path(path) -> self * * Adds +path+ as the hash dir to be looked up by the store. */ static VALUE ossl_x509store_add_path(VALUE self, VALUE dir) { X509_STORE *store; X509_LOOKUP *lookup; char *path = NULL; if(dir != Qnil){ rb_check_safe_obj(dir); path = StringValueCStr(dir); } GetX509Store(self, store); lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); if(lookup == NULL) ossl_raise(eX509StoreError, NULL); if(X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM) != 1){ ossl_raise(eX509StoreError, NULL); } return self; } /* * call-seq: * store.set_default_paths * * Configures +store+ to look up CA certificates from the system default * certificate store as needed basis. The location of the store can usually be * determined by: * * * OpenSSL::X509::DEFAULT_CERT_FILE * * OpenSSL::X509::DEFAULT_CERT_DIR */ static VALUE ossl_x509store_set_default_paths(VALUE self) { X509_STORE *store; GetX509Store(self, store); if (X509_STORE_set_default_paths(store) != 1){ ossl_raise(eX509StoreError, NULL); } return Qnil; } /* * call-seq: * store.add_cert(cert) * * Adds the OpenSSL::X509::Certificate +cert+ to the certificate store. */ static VALUE ossl_x509store_add_cert(VALUE self, VALUE arg) { X509_STORE *store; X509 *cert; cert = GetX509CertPtr(arg); /* NO NEED TO DUP */ GetX509Store(self, store); if (X509_STORE_add_cert(store, cert) != 1){ ossl_raise(eX509StoreError, NULL); } return self; } /* * call-seq: * store.add_crl(crl) -> self * * Adds the OpenSSL::X509::CRL +crl+ to the store. */ static VALUE ossl_x509store_add_crl(VALUE self, VALUE arg) { X509_STORE *store; X509_CRL *crl; crl = GetX509CRLPtr(arg); /* NO NEED TO DUP */ GetX509Store(self, store); if (X509_STORE_add_crl(store, crl) != 1){ ossl_raise(eX509StoreError, NULL); } return self; } static VALUE ossl_x509stctx_get_err(VALUE); static VALUE ossl_x509stctx_get_err_string(VALUE); static VALUE ossl_x509stctx_get_chain(VALUE); /* * call-seq: * store.verify(cert, chain = nil) -> true | false * * Performs a certificate verification on the OpenSSL::X509::Certificate +cert+. * * +chain+ can be an array of OpenSSL::X509::Certificate that is used to * construct the certificate chain. * * If a block is given, it overrides the callback set by #verify_callback=. * * After finishing the verification, the error information can be retrieved by * #error, #error_string, and the resuting complete certificate chain can be * retrieved by #chain. */ static VALUE ossl_x509store_verify(int argc, VALUE *argv, VALUE self) { VALUE cert, chain; VALUE ctx, proc, result; rb_scan_args(argc, argv, "11", &cert, &chain); ctx = rb_funcall(cX509StoreContext, rb_intern("new"), 3, self, cert, chain); proc = rb_block_given_p() ? rb_block_proc() : rb_iv_get(self, "@verify_callback"); rb_iv_set(ctx, "@verify_callback", proc); result = rb_funcall(ctx, rb_intern("verify"), 0); rb_iv_set(self, "@error", ossl_x509stctx_get_err(ctx)); rb_iv_set(self, "@error_string", ossl_x509stctx_get_err_string(ctx)); rb_iv_set(self, "@chain", ossl_x509stctx_get_chain(ctx)); return result; } /* * Public Functions */ static void ossl_x509stctx_free(void*); static const rb_data_type_t ossl_x509stctx_type = { "OpenSSL/X509/STORE_CTX", { 0, ossl_x509stctx_free, }, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, }; /* * Private functions */ static void ossl_x509stctx_free(void *ptr) { X509_STORE_CTX *ctx = ptr; if (X509_STORE_CTX_get0_untrusted(ctx)) sk_X509_pop_free(X509_STORE_CTX_get0_untrusted(ctx), X509_free); if (X509_STORE_CTX_get0_cert(ctx)) X509_free(X509_STORE_CTX_get0_cert(ctx)); X509_STORE_CTX_free(ctx); } static VALUE ossl_x509stctx_alloc(VALUE klass) { X509_STORE_CTX *ctx; VALUE obj; obj = NewX509StCtx(klass); if((ctx = X509_STORE_CTX_new()) == NULL){ ossl_raise(eX509StoreError, NULL); } SetX509StCtx(obj, ctx); return obj; } static VALUE ossl_x509stctx_new(X509_STORE_CTX *ctx) { VALUE obj; obj = NewX509StCtx(cX509StoreContext); SetX509StCtx(obj, ctx); return obj; } static VALUE ossl_x509stctx_set_flags(VALUE, VALUE); static VALUE ossl_x509stctx_set_purpose(VALUE, VALUE); static VALUE ossl_x509stctx_set_trust(VALUE, VALUE); static VALUE ossl_x509stctx_set_time(VALUE, VALUE); /* * call-seq: * StoreContext.new(store, cert = nil, chain = nil) */ static VALUE ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self) { VALUE store, cert, chain, t; X509_STORE_CTX *ctx; X509_STORE *x509st; X509 *x509 = NULL; STACK_OF(X509) *x509s = NULL; rb_scan_args(argc, argv, "12", &store, &cert, &chain); GetX509StCtx(self, ctx); SafeGetX509Store(store, x509st); if(!NIL_P(cert)) x509 = DupX509CertPtr(cert); /* NEED TO DUP */ if(!NIL_P(chain)) x509s = ossl_x509_ary2sk(chain); if(X509_STORE_CTX_init(ctx, x509st, x509, x509s) != 1){ sk_X509_pop_free(x509s, X509_free); ossl_raise(eX509StoreError, NULL); } if (!NIL_P(t = rb_iv_get(store, "@time"))) ossl_x509stctx_set_time(self, t); rb_iv_set(self, "@verify_callback", rb_iv_get(store, "@verify_callback")); rb_iv_set(self, "@cert", cert); return self; } /* * call-seq: * stctx.verify -> true | false */ static VALUE ossl_x509stctx_verify(VALUE self) { X509_STORE_CTX *ctx; GetX509StCtx(self, ctx); X509_STORE_CTX_set_ex_data(ctx, stctx_ex_verify_cb_idx, (void *)rb_iv_get(self, "@verify_callback")); switch (X509_verify_cert(ctx)) { case 1: return Qtrue; case 0: ossl_clear_error(); return Qfalse; default: ossl_raise(eX509CertError, NULL); } } /* * call-seq: * stctx.chain -> Array of X509::Certificate */ static VALUE ossl_x509stctx_get_chain(VALUE self) { X509_STORE_CTX *ctx; STACK_OF(X509) *chain; X509 *x509; int i, num; VALUE ary; GetX509StCtx(self, ctx); if((chain = X509_STORE_CTX_get0_chain(ctx)) == NULL){ return Qnil; } if((num = sk_X509_num(chain)) < 0){ OSSL_Debug("certs in chain < 0???"); return rb_ary_new(); } ary = rb_ary_new2(num); for(i = 0; i < num; i++) { x509 = sk_X509_value(chain, i); rb_ary_push(ary, ossl_x509_new(x509)); } return ary; } /* * call-seq: * stctx.error -> Integer */ static VALUE ossl_x509stctx_get_err(VALUE self) { X509_STORE_CTX *ctx; GetX509StCtx(self, ctx); return INT2NUM(X509_STORE_CTX_get_error(ctx)); } /* * call-seq: * stctx.error = error_code */ static VALUE ossl_x509stctx_set_error(VALUE self, VALUE err) { X509_STORE_CTX *ctx; GetX509StCtx(self, ctx); X509_STORE_CTX_set_error(ctx, NUM2INT(err)); return err; } /* * call-seq: * stctx.error_string -> String * * Returns the error string corresponding to the error code retrieved by #error. */ static VALUE ossl_x509stctx_get_err_string(VALUE self) { X509_STORE_CTX *ctx; long err; GetX509StCtx(self, ctx); err = X509_STORE_CTX_get_error(ctx); return rb_str_new2(X509_verify_cert_error_string(err)); } /* * call-seq: * stctx.error_depth -> Integer */ static VALUE ossl_x509stctx_get_err_depth(VALUE self) { X509_STORE_CTX *ctx; GetX509StCtx(self, ctx); return INT2NUM(X509_STORE_CTX_get_error_depth(ctx)); } /* * call-seq: * stctx.current_cert -> X509::Certificate */ static VALUE ossl_x509stctx_get_curr_cert(VALUE self) { X509_STORE_CTX *ctx; GetX509StCtx(self, ctx); return ossl_x509_new(X509_STORE_CTX_get_current_cert(ctx)); } /* * call-seq: * stctx.current_crl -> X509::CRL */ static VALUE ossl_x509stctx_get_curr_crl(VALUE self) { X509_STORE_CTX *ctx; X509_CRL *crl; GetX509StCtx(self, ctx); crl = X509_STORE_CTX_get0_current_crl(ctx); if (!crl) return Qnil; return ossl_x509crl_new(crl); } /* * call-seq: * stctx.flags = flags * * Sets the verification flags to the context. See Store#flags=. */ static VALUE ossl_x509stctx_set_flags(VALUE self, VALUE flags) { X509_STORE_CTX *store; long f = NUM2LONG(flags); GetX509StCtx(self, store); X509_STORE_CTX_set_flags(store, f); return flags; } /* * call-seq: * stctx.purpose = purpose * * Sets the purpose of the context. See Store#purpose=. */ static VALUE ossl_x509stctx_set_purpose(VALUE self, VALUE purpose) { X509_STORE_CTX *store; int p = NUM2INT(purpose); GetX509StCtx(self, store); X509_STORE_CTX_set_purpose(store, p); return purpose; } /* * call-seq: * stctx.trust = trust */ static VALUE ossl_x509stctx_set_trust(VALUE self, VALUE trust) { X509_STORE_CTX *store; int t = NUM2INT(trust); GetX509StCtx(self, store); X509_STORE_CTX_set_trust(store, t); return trust; } /* * call-seq: * stctx.time = time * * Sets the time used in the verification. If not set, the current time is used. */ static VALUE ossl_x509stctx_set_time(VALUE self, VALUE time) { X509_STORE_CTX *store; long t; t = NUM2LONG(rb_Integer(time)); GetX509StCtx(self, store); X509_STORE_CTX_set_time(store, 0, t); return time; } /* * INIT */ void Init_ossl_x509store(void) { #undef rb_intern #if 0 mOSSL = rb_define_module("OpenSSL"); eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError); mX509 = rb_define_module_under(mOSSL, "X509"); #endif /* Register ext_data slot for verify callback Proc */ stctx_ex_verify_cb_idx = X509_STORE_CTX_get_ex_new_index(0, (void *)"stctx_ex_verify_cb_idx", 0, 0, 0); if (stctx_ex_verify_cb_idx < 0) ossl_raise(eOSSLError, "X509_STORE_CTX_get_ex_new_index"); store_ex_verify_cb_idx = X509_STORE_get_ex_new_index(0, (void *)"store_ex_verify_cb_idx", 0, 0, 0); if (store_ex_verify_cb_idx < 0) ossl_raise(eOSSLError, "X509_STORE_get_ex_new_index"); eX509StoreError = rb_define_class_under(mX509, "StoreError", eOSSLError); /* Document-class: OpenSSL::X509::Store * * The X509 certificate store holds trusted CA certificates used to verify * peer certificates. * * The easiest way to create a useful certificate store is: * * cert_store = OpenSSL::X509::Store.new * cert_store.set_default_paths * * This will use your system's built-in certificates. * * If your system does not have a default set of certificates you can obtain * a set extracted from Mozilla CA certificate store by cURL maintainers * here: https://curl.haxx.se/docs/caextract.html (You may wish to use the * firefox-db2pem.sh script to extract the certificates from a local install * to avoid man-in-the-middle attacks.) * * After downloading or generating a cacert.pem from the above link you * can create a certificate store from the pem file like this: * * cert_store = OpenSSL::X509::Store.new * cert_store.add_file 'cacert.pem' * * The certificate store can be used with an SSLSocket like this: * * ssl_context = OpenSSL::SSL::SSLContext.new * ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER * ssl_context.cert_store = cert_store * * tcp_socket = TCPSocket.open 'example.com', 443 * * ssl_socket = OpenSSL::SSL::SSLSocket.new tcp_socket, ssl_context */ cX509Store = rb_define_class_under(mX509, "Store", rb_cObject); /* * The callback for additional certificate verification. It is invoked for * each untrusted certificate in the chain. * * The callback is invoked with two values, a boolean that indicates if the * pre-verification by OpenSSL has succeeded or not, and the StoreContext in * use. The callback must return either true or false. */ rb_attr(cX509Store, rb_intern("verify_callback"), 1, 0, Qfalse); /* * The error code set by the last call of #verify. */ rb_attr(cX509Store, rb_intern("error"), 1, 0, Qfalse); /* * The description for the error code set by the last call of #verify. */ rb_attr(cX509Store, rb_intern("error_string"), 1, 0, Qfalse); /* * The certificate chain constructed by the last call of #verify. */ rb_attr(cX509Store, rb_intern("chain"), 1, 0, Qfalse); rb_define_alloc_func(cX509Store, ossl_x509store_alloc); rb_define_method(cX509Store, "initialize", ossl_x509store_initialize, -1); rb_undef_method(cX509Store, "initialize_copy"); rb_define_method(cX509Store, "verify_callback=", ossl_x509store_set_vfy_cb, 1); rb_define_method(cX509Store, "flags=", ossl_x509store_set_flags, 1); rb_define_method(cX509Store, "purpose=", ossl_x509store_set_purpose, 1); rb_define_method(cX509Store, "trust=", ossl_x509store_set_trust, 1); rb_define_method(cX509Store, "time=", ossl_x509store_set_time, 1); rb_define_method(cX509Store, "add_path", ossl_x509store_add_path, 1); rb_define_method(cX509Store, "add_file", ossl_x509store_add_file, 1); rb_define_method(cX509Store, "set_default_paths", ossl_x509store_set_default_paths, 0); rb_define_method(cX509Store, "add_cert", ossl_x509store_add_cert, 1); rb_define_method(cX509Store, "add_crl", ossl_x509store_add_crl, 1); rb_define_method(cX509Store, "verify", ossl_x509store_verify, -1); /* * Document-class: OpenSSL::X509::StoreContext * * A StoreContext is used while validating a single certificate and holds * the status involved. */ cX509StoreContext = rb_define_class_under(mX509,"StoreContext", rb_cObject); rb_define_alloc_func(cX509StoreContext, ossl_x509stctx_alloc); rb_define_method(cX509StoreContext, "initialize", ossl_x509stctx_initialize, -1); rb_undef_method(cX509StoreContext, "initialize_copy"); rb_define_method(cX509StoreContext, "verify", ossl_x509stctx_verify, 0); rb_define_method(cX509StoreContext, "chain", ossl_x509stctx_get_chain,0); rb_define_method(cX509StoreContext, "error", ossl_x509stctx_get_err, 0); rb_define_method(cX509StoreContext, "error=", ossl_x509stctx_set_error, 1); rb_define_method(cX509StoreContext, "error_string", ossl_x509stctx_get_err_string,0); rb_define_method(cX509StoreContext, "error_depth", ossl_x509stctx_get_err_depth, 0); rb_define_method(cX509StoreContext, "current_cert", ossl_x509stctx_get_curr_cert, 0); rb_define_method(cX509StoreContext, "current_crl", ossl_x509stctx_get_curr_crl, 0); rb_define_method(cX509StoreContext, "flags=", ossl_x509stctx_set_flags, 1); rb_define_method(cX509StoreContext, "purpose=", ossl_x509stctx_set_purpose, 1); rb_define_method(cX509StoreContext, "trust=", ossl_x509stctx_set_trust, 1); rb_define_method(cX509StoreContext, "time=", ossl_x509stctx_set_time, 1); } openssl-2.0.9/ext/openssl/ruby_missing.h000066400000000000000000000012751336157045000203640ustar00rootroot00000000000000/* * 'OpenSSL for Ruby' project * Copyright (C) 2001-2003 Michal Rokos * All rights reserved. */ /* * This program is licensed under the same licence as Ruby. * (See the file 'LICENCE'.) */ #if !defined(_OSSL_RUBY_MISSING_H_) #define _OSSL_RUBY_MISSING_H_ #define rb_define_copy_func(klass, func) \ rb_define_method((klass), "initialize_copy", (func), 1) #define FPTR_TO_FD(fptr) ((fptr)->fd) /* Ruby 2.4 */ #ifndef RB_INTEGER_TYPE_P # define RB_INTEGER_TYPE_P(obj) (RB_FIXNUM_P(obj) || RB_TYPE_P(obj, T_BIGNUM)) #endif /* Ruby 2.5 */ #ifndef ST2FIX # define RB_ST2FIX(h) LONG2FIX((long)(h)) # define ST2FIX(h) RB_ST2FIX(h) #endif #endif /* _OSSL_RUBY_MISSING_H_ */ openssl-2.0.9/lib/000077500000000000000000000000001336157045000137575ustar00rootroot00000000000000openssl-2.0.9/lib/openssl.rb000066400000000000000000000006751336157045000157770ustar00rootroot00000000000000# frozen_string_literal: false =begin = Info 'OpenSSL for Ruby 2' project Copyright (C) 2002 Michal Rokos All rights reserved. = Licence This program is licensed under the same licence as Ruby. (See the file 'LICENCE'.) =end require 'openssl.so' require 'openssl/bn' require 'openssl/pkey' require 'openssl/cipher' require 'openssl/config' require 'openssl/digest' require 'openssl/x509' require 'openssl/ssl' openssl-2.0.9/lib/openssl/000077500000000000000000000000001336157045000154425ustar00rootroot00000000000000openssl-2.0.9/lib/openssl/bn.rb000066400000000000000000000012761336157045000163740ustar00rootroot00000000000000# 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 # 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 openssl-2.0.9/lib/openssl/buffering.rb000066400000000000000000000234001336157045000177350ustar00rootroot00000000000000# coding: binary # frozen_string_literal: false #-- #= Info # 'OpenSSL for Ruby 2' project # Copyright (C) 2001 GOTOU YUUZOU # 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 `exception: false`, the options hash allows you to indicate # that read_nonblock should not raise an IO::Wait*able exception, but # return the symbol :wait_writable or :wait_readable instead. 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 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 +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 `exception: false`, the options hash allows you to 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 # 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 openssl-2.0.9/lib/openssl/cipher.rb000066400000000000000000000033211336157045000172400ustar00rootroot00000000000000# frozen_string_literal: false #-- # = Ruby-space predefined Cipher subclasses # # = Info # 'OpenSSL for Ruby 2' project # Copyright (C) 2002 Michal Rokos # 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 openssl-2.0.9/lib/openssl/config.rb000066400000000000000000000277421336157045000172500ustar00rootroot00000000000000# frozen_string_literal: false =begin = Ruby-space definitions that completes C-space funcs for Config = Info Copyright (C) 2010 Hiroshi Nakamura = 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') # #=> # # 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 openssl-2.0.9/lib/openssl/digest.rb000066400000000000000000000034401336157045000172470ustar00rootroot00000000000000# frozen_string_literal: false #-- # = Ruby-space predefined Digest subclasses # # = Info # 'OpenSSL for Ruby 2' project # Copyright (C) 2002 Michal Rokos # 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) if OPENSSL_VERSION_NUMBER < 0x10100000 alg += %w(DSS DSS1 SHA) end 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. # 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 openssl-2.0.9/lib/openssl/pkey.rb000066400000000000000000000024231336157045000167400ustar00rootroot00000000000000# frozen_string_literal: false module OpenSSL module PKey if defined?(OpenSSL::PKey::DH) class DH # :nodoc: DEFAULT_1024 = new <<-_end_of_pem_ -----BEGIN DH PARAMETERS----- MIGHAoGBAJ0lOVy0VIr/JebWn0zDwY2h+rqITFOpdNr6ugsgvkDXuucdcChhYExJ AV/ZD2AWPbrTqV76mGRgJg4EddgT1zG0jq3rnFdMj2XzkBYx3BVvfR0Arnby0RHR T4h7KZ/2zmjvV+eF8kBUHBJAojUlzxKj4QeO2x20FP9X5xmNUXeDAgEC -----END DH PARAMETERS----- _end_of_pem_ # :nodoc: DEFAULT_2048 = 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_ end # :nodoc: DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| warn "using default DH parameters." if $VERBOSE case keylen when 1024 then OpenSSL::PKey::DH::DEFAULT_1024 when 2048 then OpenSSL::PKey::DH::DEFAULT_2048 else nil end } else DEFAULT_TMP_DH_CALLBACK = nil end end end openssl-2.0.9/lib/openssl/ssl.rb000066400000000000000000000301511336157045000165700ustar00rootroot00000000000000# frozen_string_literal: false =begin = Info 'OpenSSL for Ruby 2' project Copyright (C) 2001 GOTOU YUUZOU 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" module OpenSSL module SSL class SSLContext DEFAULT_PARAMS = { # :nodoc: :ssl_version => "SSLv23", :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 if defined?(OpenSSL::SSL::OP_NO_COMPRESSION) opts |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 opts }.call } 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 if ExtConfig::HAVE_TLSEXT_HOST_NAME # call-seq: # SSLContext.new => ctx # SSLContext.new(:TLSv1) => ctx # SSLContext.new("SSLv23_client") => ctx # # You can get a list of valid methods with OpenSSL::SSL::SSLContext::METHODS 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) 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 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 if ExtConfig::HAVE_TLSEXT_HOST_NAME attr_reader :hostname end # 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::PKey::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 openssl-2.0.9/lib/openssl/x509.rb000066400000000000000000000120141336157045000164720ustar00rootroot00000000000000# 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 # 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 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 openssl-2.0.9/openssl.gemspec000066400000000000000000000020021336157045000162330ustar00rootroot00000000000000Gem::Specification.new do |spec| spec.name = "openssl" spec.version = "2.0.9" spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"] spec.email = ["ruby-core@ruby-lang.org"] spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.} spec.description = %q{It wraps the OpenSSL library.} spec.homepage = "https://www.ruby-lang.org/" spec.license = "Ruby" spec.files = Dir["lib/**/*.rb", "ext/**/*.{c,h,rb}", "*.md", "BSDL", "LICENSE.txt"] spec.require_paths = ["lib"] spec.extensions = ["ext/openssl/extconf.rb"] spec.extra_rdoc_files = Dir["*.md"] spec.rdoc_options = ["--main", "README.md"] spec.required_ruby_version = ">= 2.3.0" spec.add_development_dependency "rake" spec.add_development_dependency "rake-compiler" spec.add_development_dependency "test-unit", "~> 3.0" spec.add_development_dependency "rdoc" spec.metadata["msys2_mingw_dependencies"] = "openssl" end openssl-2.0.9/sample/000077500000000000000000000000001336157045000144725ustar00rootroot00000000000000openssl-2.0.9/sample/c_rehash.rb000066400000000000000000000072441336157045000166020ustar00rootroot00000000000000#!/usr/bin/env ruby require 'openssl' require 'digest/md5' class CHashDir include Enumerable def initialize(dirpath) @dirpath = dirpath @fingerprint_cache = @cert_cache = @crl_cache = nil end def hash_dir(silent = false) # ToDo: Should lock the directory... @silent = silent @fingerprint_cache = Hash.new @cert_cache = Hash.new @crl_cache = Hash.new do_hash_dir end def get_certs(name = nil) if name @cert_cache[hash_name(name)] else @cert_cache.values.flatten end end def get_crls(name = nil) if name @crl_cache[hash_name(name)] else @crl_cache.values.flatten end end def delete_crl(crl) File.unlink(crl_filename(crl)) hash_dir(true) end def add_crl(crl) File.open(crl_filename(crl), "w") do |f| f << crl.to_pem end hash_dir(true) end def load_pem_file(filepath) str = File.read(filepath) begin OpenSSL::X509::Certificate.new(str) rescue begin OpenSSL::X509::CRL.new(str) rescue begin OpenSSL::X509::Request.new(str) rescue nil end end end end private def crl_filename(crl) path(hash_name(crl.issuer)) + '.pem' end def do_hash_dir Dir.chdir(@dirpath) do delete_symlink Dir.glob('*.pem') do |pemfile| cert = load_pem_file(pemfile) case cert when OpenSSL::X509::Certificate link_hash_cert(pemfile, cert) when OpenSSL::X509::CRL link_hash_crl(pemfile, cert) else STDERR.puts("WARNING: #{pemfile} does not contain a certificate or CRL: skipping") unless @silent end end end end def delete_symlink Dir.entries(".").each do |entry| next unless /^[\da-f]+\.r{0,1}\d+$/ =~ entry File.unlink(entry) if FileTest.symlink?(entry) end end def link_hash_cert(org_filename, cert) name_hash = hash_name(cert.subject) fingerprint = fingerprint(cert.to_der) filepath = link_hash(org_filename, name_hash, fingerprint) { |idx| "#{name_hash}.#{idx}" } unless filepath unless @silent STDERR.puts("WARNING: Skipping duplicate certificate #{org_filename}") end else (@cert_cache[name_hash] ||= []) << path(filepath) end end def link_hash_crl(org_filename, crl) name_hash = hash_name(crl.issuer) fingerprint = fingerprint(crl.to_der) filepath = link_hash(org_filename, name_hash, fingerprint) { |idx| "#{name_hash}.r#{idx}" } unless filepath unless @silent STDERR.puts("WARNING: Skipping duplicate CRL #{org_filename}") end else (@crl_cache[name_hash] ||= []) << path(filepath) end end def link_hash(org_filename, name, fingerprint) idx = 0 filepath = nil while true filepath = yield(idx) break unless FileTest.symlink?(filepath) or FileTest.exist?(filepath) if @fingerprint_cache[filepath] == fingerprint return false end idx += 1 end STDOUT.puts("#{org_filename} => #{filepath}") unless @silent symlink(org_filename, filepath) @fingerprint_cache[filepath] = fingerprint filepath end def symlink(from, to) begin File.symlink(from, to) rescue File.open(to, "w") do |f| f << File.read(from) end end end def path(filename) File.join(@dirpath, filename) end def hash_name(name) sprintf("%x", name.hash) end def fingerprint(der) Digest::MD5.hexdigest(der).upcase end end if $0 == __FILE__ dirlist = ARGV dirlist << '/usr/ssl/certs' if dirlist.empty? dirlist.each do |dir| CHashDir.new(dir).hash_dir end end openssl-2.0.9/sample/cert2text.rb000066400000000000000000000005701336157045000167450ustar00rootroot00000000000000#!/usr/bin/env ruby require 'openssl' include OpenSSL::X509 def cert2text(cert_str) [Certificate, CRL, Request].each do |klass| begin puts klass.new(cert_str).to_text return rescue end end raise ArgumentError.new('Unknown format.') end if ARGV.empty? cert2text(STDIN.read) else ARGV.each do |file| cert2text(File.read(file)) end end openssl-2.0.9/sample/certstore.rb000066400000000000000000000067501336157045000170410ustar00rootroot00000000000000require 'c_rehash' require 'crlstore' class CertStore include OpenSSL include X509 attr_reader :self_signed_ca attr_reader :other_ca attr_reader :ee attr_reader :crl attr_reader :request def initialize(certs_dir) @certs_dir = certs_dir @c_store = CHashDir.new(@certs_dir) @c_store.hash_dir(true) @crl_store = CrlStore.new(@c_store) @x509store = Store.new @self_signed_ca = @other_ca = @ee = @crl = nil # Uncomment this line to let OpenSSL to check CRL for each certs. # @x509store.flags = V_FLAG_CRL_CHECK | V_FLAG_CRL_CHECK_ALL add_path scan_certs end def generate_cert(filename) @c_store.load_pem_file(filename) end def verify(cert) error, crl_map = do_verify(cert) if error [[false, cert, crl_map[cert.subject], error]] else @x509store.chain.collect { |c| [true, c, crl_map[c.subject], nil] } end end def match_cert(cert1, cert2) (cert1.issuer.cmp(cert2.issuer) == 0) and cert1.serial == cert2.serial end def is_ca?(cert) case guess_cert_type(cert) when CERT_TYPE_SELF_SIGNED true when CERT_TYPE_OTHER true else false end end def scan_certs @self_signed_ca = [] @other_ca = [] @ee = [] @crl = [] @request = [] load_certs end private def add_path @x509store.add_path(@certs_dir) end def do_verify(cert) error_map = {} crl_map = {} result = @x509store.verify(cert) do |ok, ctx| cert = ctx.current_cert if ctx.current_crl crl_map[cert.subject] = true end if ok if !ctx.current_crl if crl = @crl_store.find_crl(cert) crl_map[cert.subject] = true if crl.revoked.find { |revoked| revoked.serial == cert.serial } ok = false error_string = 'certification revoked' end end end end error_map[cert.subject] = error_string if error_string ok end error = if result nil else error_map[cert.subject] || @x509store.error_string end return error, crl_map end def load_certs @c_store.get_certs.each do |certfile| cert = generate_cert(certfile) case guess_cert_type(cert) when CERT_TYPE_SELF_SIGNED @self_signed_ca << cert when CERT_TYPE_OTHER @other_ca << cert when CERT_TYPE_EE @ee << cert else raise "Unknown cert type." end end @c_store.get_crls.each do |crlfile| @crl << generate_cert(crlfile) end end CERT_TYPE_SELF_SIGNED = 0 CERT_TYPE_OTHER = 1 CERT_TYPE_EE = 2 def guess_cert_type(cert) ca = self_signed = is_cert_self_signed(cert) cert.extensions.each do |ext| # Ignores criticality of extensions. It's 'guess'ing. case ext.oid when 'basicConstraints' /CA:(TRUE|FALSE), pathlen:(\d+)/ =~ ext.value ca = ($1 == 'TRUE') unless ca when 'keyUsage' usage = ext.value.split(/\s*,\s*/) ca = usage.include?('Certificate Sign') unless ca when 'nsCertType' usage = ext.value.split(/\s*,\s*/) ca = usage.include?('SSL CA') unless ca end end if ca if self_signed CERT_TYPE_SELF_SIGNED else CERT_TYPE_OTHER end else CERT_TYPE_EE end end def is_cert_self_signed(cert) # cert.subject.cmp(cert.issuer) == 0 cert.subject.to_s == cert.issuer.to_s end end if $0 == __FILE__ c = CertStore.new("trust_certs") end openssl-2.0.9/sample/cipher.rb000066400000000000000000000022211336157045000162660ustar00rootroot00000000000000#!/usr/bin/env ruby require 'openssl' def crypt_by_password(alg, pass, salt, text) puts "--Setup--" puts %(cipher alg: "#{alg}") puts %(plain text: "#{text}") puts %(password: "#{pass}") puts %(salt: "#{salt}") puts puts "--Encrypting--" enc = OpenSSL::Cipher.new(alg) enc.encrypt enc.pkcs5_keyivgen(pass, salt) cipher = enc.update(text) cipher << enc.final puts %(encrypted text: #{cipher.inspect}) puts puts "--Decrypting--" dec = OpenSSL::Cipher.new(alg) dec.decrypt dec.pkcs5_keyivgen(pass, salt) plain = dec.update(cipher) plain << dec.final puts %(decrypted text: "#{plain}") puts end def ciphers ciphers = OpenSSL::Cipher.ciphers.sort ciphers.each{|i| if i.upcase != i && ciphers.include?(i.upcase) ciphers.delete(i) end } return ciphers end puts "Supported ciphers in #{OpenSSL::OPENSSL_VERSION}:" ciphers.each_with_index{|name, i| printf("%-15s", name) puts if (i + 1) % 5 == 0 } puts puts alg = ARGV.shift || ciphers.first pass = "secret password" salt = "8 octets" # or nil text = "abcdefghijklmnopqrstuvwxyz" crypt_by_password(alg, pass, salt, text) openssl-2.0.9/sample/crlstore.rb000066400000000000000000000045501336157045000166600ustar00rootroot00000000000000begin require 'http-access2' rescue LoadError STDERR.puts("Cannot load http-access2. CRL might not be fetched.") end require 'c_rehash' class CrlStore def initialize(c_store) @c_store = c_store @c_store.hash_dir(true) end def find_crl(cert) do_find_crl(cert) end private def do_find_crl(cert) unless ca = find_ca(cert) return nil end unless crlfiles = @c_store.get_crls(ca.subject) if crl = renew_crl(cert, ca) @c_store.add_crl(crl) return crl end return nil end crlfiles.each do |crlfile| next unless crl = load_crl(crlfile) if crl.next_update < Time.now if new_crl = renew_crl(cert, ca) @c_store.delete_crl(crl) @c_store.add_crl(new_crl) crl = new_crl end end if check_valid(crl, ca) return crl end end nil end def find_ca(cert) @c_store.get_certs(cert.issuer).each do |cafile| ca = load_cert(cafile) if cert.verify(ca.public_key) return ca end end nil end def fetch(location) if /\AURI:(.*)\z/ =~ location begin c = HTTPAccess2::Client.new(ENV['http_proxy'] || ENV['HTTP_PROXY']) c.get_content($1) rescue NameError, StandardError nil end else nil end end def load_cert(certfile) load_cert_str(File.read(certfile)) end def load_crl(crlfile) load_crl_str(File.read(crlfile)) end def load_cert_str(cert_str) OpenSSL::X509::Certificate.new(cert_str) end def load_crl_str(crl_str) OpenSSL::X509::CRL.new(crl_str) end def check_valid(crl, ca) unless crl.verify(ca.public_key) return false end crl.last_update <= Time.now end RE_CDP = /\AcrlDistributionPoints\z/ def get_cdp(cert) if cdp_ext = cert.extensions.find { |ext| RE_CDP =~ ext.oid } cdp_ext.value.chomp else false end end def renew_crl(cert, ca) if cdp = get_cdp(cert) if new_crl_str = fetch(cdp) new_crl = load_crl_str(new_crl_str) if check_valid(new_crl, ca) return new_crl end end end false end end if $0 == __FILE__ dir = "trust_certs" c_store = CHashDir.new(dir) s = CrlStore.new(c_store) c = OpenSSL::X509::Certificate.new(File.read("cert_store/google_codesign.pem")) p s.find_crl(c) end openssl-2.0.9/sample/echo_cli.rb000066400000000000000000000021141336157045000165620ustar00rootroot00000000000000#!/usr/bin/env ruby require 'socket' require 'openssl' require 'optparse' options = ARGV.getopts("p:c:k:C:") host = ARGV[0] || "localhost" port = options["p"] || "2000" cert_file = options["c"] key_file = options["k"] ca_path = options["C"] ctx = OpenSSL::SSL::SSLContext.new() if cert_file && key_file ctx.cert = OpenSSL::X509::Certificate.new(File::read(cert_file)) ctx.key = OpenSSL::PKey::RSA.new(File::read(key_file)) end if ca_path ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER ctx.ca_path = ca_path else $stderr.puts "!!! WARNING: PEER CERTIFICATE WON'T BE VERIFIED !!!" end s = TCPSocket.new(host, port) ssl = OpenSSL::SSL::SSLSocket.new(s, ctx) ssl.connect # start SSL session p ssl.peer_cert errors = Hash.new OpenSSL::X509.constants.grep(/^V_(ERR_|OK)/).each do |name| errors[OpenSSL::X509.const_get(name)] = name end p errors[ssl.verify_result] ssl.sync_close = true # if true the underlying socket will be # closed in SSLSocket#close. (default: false) while line = $stdin.gets ssl.write line puts ssl.gets.inspect end ssl.close openssl-2.0.9/sample/echo_svr.rb000066400000000000000000000034431336157045000166330ustar00rootroot00000000000000#!/usr/bin/env ruby require 'socket' require 'openssl' require 'optparse' options = ARGV.getopts("p:c:k:C:") port = options["p"] || "2000" cert_file = options["c"] key_file = options["k"] ca_path = options["C"] if cert_file && key_file cert = OpenSSL::X509::Certificate.new(File::read(cert_file)) key = OpenSSL::PKey::RSA.new(File::read(key_file)) else key = OpenSSL::PKey::RSA.new(512){ print "." } puts cert = OpenSSL::X509::Certificate.new cert.version = 2 cert.serial = 0 name = OpenSSL::X509::Name.new([["C","JP"],["O","TEST"],["CN","localhost"]]) cert.subject = name cert.issuer = name cert.not_before = Time.now cert.not_after = Time.now + 3600 cert.public_key = key.public_key ef = OpenSSL::X509::ExtensionFactory.new(nil,cert) cert.extensions = [ ef.create_extension("basicConstraints","CA:FALSE"), ef.create_extension("subjectKeyIdentifier","hash"), ef.create_extension("extendedKeyUsage","serverAuth"), ef.create_extension("keyUsage", "keyEncipherment,dataEncipherment,digitalSignature") ] ef.issuer_certificate = cert cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") cert.sign(key, OpenSSL::Digest::SHA1.new) end ctx = OpenSSL::SSL::SSLContext.new() ctx.key = key ctx.cert = cert if ca_path ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT ctx.ca_path = ca_path else $stderr.puts "!!! WARNING: PEER CERTIFICATE WON'T BE VERIFIED !!!" end tcps = TCPServer.new(port) ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx) loop do ns = ssls.accept puts "connected from #{ns.peeraddr}" while line = ns.gets puts line.inspect ns.write line end puts "connection closed" ns.close end openssl-2.0.9/sample/gen_csr.rb000066400000000000000000000020411336157045000164340ustar00rootroot00000000000000#!/usr/bin/env ruby require 'optparse' require 'openssl' include OpenSSL def usage myname = File::basename($0) $stderr.puts < marshal_error ignore_stderr = nil end if res.is_a?(String) pend res elsif res if bt = res.backtrace bt.each do |l| l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"} end bt.concat(caller) else res.set_backtrace(caller) end raise res end # really is it succeed? unless ignore_stderr # the body of assert_separately must not output anything to detect error assert_equal("", stderr, "assert_separately failed with error message") end assert_equal(0, status, "assert_separately failed: '#{stderr}'") raise marshal_error if marshal_error end def message msg = nil, ending = ".", &default proc { msg = msg.call.chomp(".") if Proc === msg custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty? "#{custom_message}#{default.call}#{ending}" } end # threads should respond to shift method. # Array can be used. def assert_join_threads(threads, message = nil) errs = [] values = [] while th = threads.shift begin values << th.value rescue Exception errs << [th, $!] end end if !errs.empty? msg = "exceptions on #{errs.length} threads:\n" + errs.map {|t, err| "#{t.inspect}:\n" + err.backtrace.map.with_index {|line, i| if i == 0 "#{line}: #{err.message} (#{err.class})" else "\tfrom #{line}" end }.join("\n") }.join("\n---\n") if message msg = "#{message}\n#{msg}" end raise Test::Unit::AssertionFailedError, msg end values end def mu_pp(obj) #:nodoc: obj.pretty_inspect.chomp end # :call-seq: # assert_raise_with_message(exception, expected, msg = nil, &block) # #Tests if the given block raises an exception with the expected #message. # # assert_raise_with_message(RuntimeError, "foo") do # nil #Fails, no Exceptions are raised # end # # assert_raise_with_message(RuntimeError, "foo") do # raise ArgumentError, "foo" #Fails, different Exception is raised # end # # assert_raise_with_message(RuntimeError, "foo") do # raise "bar" #Fails, RuntimeError is raised but the message differs # end # # assert_raise_with_message(RuntimeError, "foo") do # raise "foo" #Raises RuntimeError with the message, so assertion succeeds # end def assert_raise_with_message(exception, expected, msg = nil, &block) case expected when String assert = :assert_equal when Regexp assert = :assert_match else raise TypeError, "Expected #{expected.inspect} to be a kind of String or Regexp, not #{expected.class}" end ex = m = nil ex = assert_raise(exception, msg || "Exception(#{exception}) with message matches to #{expected.inspect}") do yield end m = ex.message msg = message(msg, "") {"Expected Exception(#{exception}) was raised, but the message doesn't match"} if assert == :assert_equal assert_equal(expected, m, msg) else msg = message(msg) { "Expected #{mu_pp expected} to match #{mu_pp m}" } assert expected =~ m, msg block.binding.eval("proc{|_|$~=_}").call($~) end ex end end end end openssl-2.0.9/test/fixtures/000077500000000000000000000000001336157045000160415ustar00rootroot00000000000000openssl-2.0.9/test/fixtures/pkey/000077500000000000000000000000001336157045000170115ustar00rootroot00000000000000openssl-2.0.9/test/fixtures/pkey/dh1024.pem000066400000000000000000000003651336157045000204220ustar00rootroot00000000000000-----BEGIN DH PARAMETERS----- MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0 pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC -----END DH PARAMETERS----- openssl-2.0.9/test/fixtures/pkey/dsa1024.pem000066400000000000000000000012341336157045000205720ustar00rootroot00000000000000-----BEGIN DSA PRIVATE KEY----- MIIBugIBAAKBgQCH9aAoXvWWThIjkA6D+nI1F9ksF9iDq594rkiGNOT9sPDOdB+n D+qeeeeloRlj19ymCSADPI0ZLRgkchkAEnY2RnqnhHOjVf/roGgRbW+iQDMbQ9wa /pvc6/fAbsu1goE1hBYjm98/sZEeXavj8tR56IXnjF1b6Nx0+sgeUKFKEQIVAMiz 4BJUFeTtddyM4uadBM7HKLPRAoGAZdLBSYNGiij7vAjesF5mGUKTIgPd+JKuBEDx OaBclsgfdoyoF/TMOkIty+PVlYD+//Vl2xnoUEIRaMXHwHfm0r2xUX++oeRaSScg YizJdUxe5jvBuBszGPRc/mGpb9YvP0sB+FL1KmuxYmdODfCe51zl8uM/CVhouJ3w DjmRGscCgYAuFlfC7p+e8huCKydfcv/beftqjewiOPpQ3u5uI6KPCtCJPpDhs3+4 IihH2cPsAlqwGF4tlibW1+/z/OZ1AZinPK3y7b2jSJASEaPeEltVzB92hcd1khk2 jTYcmSsV4VddplOPK9czytR/GbbibxsrhhgZUbd8LPbvIgaiadJ1PgIUBnJ/5vN2 CVArsEzlPUCbohPvZnE= -----END DSA PRIVATE KEY----- openssl-2.0.9/test/fixtures/pkey/dsa256.pem000066400000000000000000000006241336157045000205220ustar00rootroot00000000000000-----BEGIN DSA PRIVATE KEY----- MIH3AgEAAkEAhk2libbY2a8y2Pt21+YPYGZeW6wzaW2yfj5oiClXro9XMR7XWLkE 9B7XxLNFCS2gmCCdMsMW1HulaHtLFQmB2wIVAM43JZrcgpu6ajZ01VkLc93gu/Ed AkAOhujZrrKV5CzBKutKLb0GVyVWmdC7InoNSMZEeGU72rT96IjM59YzoqmD0pGM 3I1o4cGqg1D1DfM1rQlnN1eSAkBq6xXfEDwJ1mLNxF6q8Zm/ugFYWR5xcX/3wFiT b4+EjHP/DbNh9Vm5wcfnDBJ1zKvrMEf2xqngYdrV/3CiGJeKAhRvL57QvJZcQGvn ISNX5cMzFHRW3Q== -----END DSA PRIVATE KEY----- openssl-2.0.9/test/fixtures/pkey/dsa512.pem000066400000000000000000000006241336157045000205150ustar00rootroot00000000000000-----BEGIN DSA PRIVATE KEY----- MIH4AgEAAkEA5lB4GvEwjrsMlGDqGsxrbqeFRh6o9OWt6FgTYiEEHaOYhkIxv0Ok RZPDNwOG997mDjBnvDJ1i56OmS3MbTnovwIVAJgub/aDrSDB4DZGH7UyarcaGy6D AkB9HdFw/3td8K4l1FZHv7TCZeJ3ZLb7dF3TWoGUP003RCqoji3/lHdKoVdTQNuR S/m6DlCwhjRjiQ/lBRgCLCcaAkEAjN891JBjzpMj4bWgsACmMggFf57DS0Ti+5++ Q1VB8qkJN7rA7/2HrCR3gTsWNb1YhAsnFsoeRscC+LxXoXi9OAIUBG98h4tilg6S 55jreJD3Se3slps= -----END DSA PRIVATE KEY----- openssl-2.0.9/test/fixtures/pkey/p256.pem000066400000000000000000000003431336157045000202100ustar00rootroot00000000000000-----BEGIN EC PRIVATE KEY----- MHcCAQEEIID49FDqcf1O1eO8saTgG70UbXQw9Fqwseliit2aWhH1oAoGCCqGSM49 AwEHoUQDQgAEFglk2c+oVUIKQ64eZG9bhLNPWB7lSZ/ArK41eGy5wAzU/0G51Xtt CeBUl+MahZtn9fO1JKdF4qJmS39dXnpENg== -----END EC PRIVATE KEY----- openssl-2.0.9/test/fixtures/pkey/rsa1024.pem000066400000000000000000000015731336157045000206160ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7Cx aKPERYHsk4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/ Q3geLv8ZD9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQAB AoGBAKSl/MQarye1yOysqX6P8fDFQt68VvtXkNmlSiKOGuzyho0M+UVSFcs6k1L0 maDE25AMZUiGzuWHyaU55d7RXDgeskDMakD1v6ZejYtxJkSXbETOTLDwUWTn618T gnb17tU1jktUtU67xK/08i/XodlgnQhs6VoHTuCh3Hu77O6RAkEA7+gxqBuZR572 74/akiW/SuXm0SXPEviyO1MuSRwtI87B02D0qgV8D1UHRm4AhMnJ8MCs1809kMQE JiQUCrp9mQJBANlt2ngBO14us6NnhuAseFDTBzCHXwUUu1YKHpMMmxpnGqaldGgX sOZB3lgJsT9VlGf3YGYdkLTNVbogQKlKpB8CQQDiSwkb4vyQfDe8/NpU5Not0fII 8jsDUCb+opWUTMmfbxWRR3FBNu8wnym/m19N4fFj8LqYzHX4KY0oVPu6qvJxAkEA wa5snNekFcqONLIE4G5cosrIrb74sqL8GbGb+KuTAprzj5z1K8Bm0UW9lTjVDjDi qRYgZfZSL+x1P/54+xTFSwJAY1FxA/N3QPCXCjPh5YqFxAMQs2VVYTfg+t0MEcJD dPMQD5JX6g5HKnHFg2mZtoXQrWmJSn7p8GJK8yNTopEErA== -----END RSA PRIVATE KEY----- openssl-2.0.9/test/fixtures/pkey/rsa2048.pem000066400000000000000000000032171336157045000206220ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAuV9ht9J7k4NBs38jOXvvTKY9gW8nLICSno5EETR1cuF7i4pN s9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enenfzq/t/e/1IRW0wkJUJUFQign 4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWmqbjs07JbuS4QQGGXLc+Su96D kYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v68JkRFIhdGlb6JL8fllf/A/bl NwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX9KZYcU00mOX+fdxOSnGqS/8J DRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wIDAQABAoIBAAzsamqfYQAqwXTb I0CJtGg6msUgU7HVkOM+9d3hM2L791oGHV6xBAdpXW2H8LgvZHJ8eOeSghR8+dgq PIqAffo4x1Oma+FOg3A0fb0evyiACyrOk+EcBdbBeLo/LcvahBtqnDfiUMQTpy6V seSoFCwuN91TSCeGIsDpRjbG1vxZgtx+uI+oH5+ytqJOmfCksRDCkMglGkzyfcl0 Xc5CUhIJ0my53xijEUQl19rtWdMnNnnkdbG8PT3LZlOta5Do86BElzUYka0C6dUc VsBDQ0Nup0P6rEQgy7tephHoRlUGTYamsajGJaAo1F3IQVIrRSuagi7+YpSpCqsW wORqorkCgYEA7RdX6MDVrbw7LePnhyuaqTiMK+055/R1TqhB1JvvxJ1CXk2rDL6G 0TLHQ7oGofd5LYiemg4ZVtWdJe43BPZlVgT6lvL/iGo8JnrncB9Da6L7nrq/+Rvj XGjf1qODCK+LmreZWEsaLPURIoR/Ewwxb9J2zd0CaMjeTwafJo1CZvcCgYEAyCgb aqoWvUecX8VvARfuA593Lsi50t4MEArnOXXcd1RnXoZWhbx5rgO8/ATKfXr0BK/n h2GF9PfKzHFm/4V6e82OL7gu/kLy2u9bXN74vOvWFL5NOrOKPM7Kg+9I131kNYOw Ivnr/VtHE5s0dY7JChYWE1F3vArrOw3T00a4CXUCgYEA0SqY+dS2LvIzW4cHCe9k IQqsT0yYm5TFsUEr4sA3xcPfe4cV8sZb9k/QEGYb1+SWWZ+AHPV3UW5fl8kTbSNb v4ng8i8rVVQ0ANbJO9e5CUrepein2MPL0AkOATR8M7t7dGGpvYV0cFk8ZrFx0oId U0PgYDotF/iueBWlbsOM430CgYEAqYI95dFyPI5/AiSkY5queeb8+mQH62sdcCCr vd/w/CZA/K5sbAo4SoTj8dLk4evU6HtIa0DOP63y071eaxvRpTNqLUOgmLh+D6gS Cc7TfLuFrD+WDBatBd5jZ+SoHccVrLR/4L8jeodo5FPW05A+9gnKXEXsTxY4LOUC 9bS4e1kCgYAqVXZh63JsMwoaxCYmQ66eJojKa47VNrOeIZDZvd2BPVf30glBOT41 gBoDG3WMPZoQj9pb7uMcrnvs4APj2FIhMU8U15LcPAj59cD6S6rWnAxO8NFK7HQG 4Jxg3JNNf8ErQoCHb1B3oVdXJkmbJkARoDpBKmTCgKtP8ADYLmVPQw== -----END RSA PRIVATE KEY----- openssl-2.0.9/test/test_asn1.rb000066400000000000000000000561371336157045000164320ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestASN1 < OpenSSL::TestCase def test_decode subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA") key = Fixtures.pkey("rsa1024") now = Time.at(Time.now.to_i) # suppress usec s = 0xdeadbeafdeadbeafdeadbeafdeadbeaf exts = [ ["basicConstraints","CA:TRUE,pathlen:1",true], ["keyUsage","keyCertSign, cRLSign",true], ["subjectKeyIdentifier","hash",false], ] dgst = OpenSSL::Digest::SHA1.new cert = OpenSSL::TestUtils.issue_cert( subj, key, s, exts, nil, nil, digest: dgst, not_before: now, not_after: now+3600) asn1 = OpenSSL::ASN1.decode(cert) assert_equal(OpenSSL::ASN1::Sequence, asn1.class) assert_equal(3, asn1.value.size) tbs_cert, sig_alg, sig_val = *asn1.value assert_equal(OpenSSL::ASN1::Sequence, tbs_cert.class) assert_equal(8, tbs_cert.value.size) version = tbs_cert.value[0] assert_equal(:CONTEXT_SPECIFIC, version.tag_class) assert_equal(0, version.tag) assert_equal(1, version.value.size) assert_equal(OpenSSL::ASN1::Integer, version.value[0].class) assert_equal(2, version.value[0].value) serial = tbs_cert.value[1] assert_equal(OpenSSL::ASN1::Integer, serial.class) assert_equal(0xdeadbeafdeadbeafdeadbeafdeadbeaf, serial.value) sig = tbs_cert.value[2] assert_equal(OpenSSL::ASN1::Sequence, sig.class) assert_equal(2, sig.value.size) assert_equal(OpenSSL::ASN1::ObjectId, sig.value[0].class) assert_equal("1.2.840.113549.1.1.5", sig.value[0].oid) assert_equal(OpenSSL::ASN1::Null, sig.value[1].class) dn = tbs_cert.value[3] # issuer assert_equal(subj.hash, OpenSSL::X509::Name.new(dn).hash) assert_equal(OpenSSL::ASN1::Sequence, dn.class) assert_equal(3, dn.value.size) assert_equal(OpenSSL::ASN1::Set, dn.value[0].class) assert_equal(OpenSSL::ASN1::Set, dn.value[1].class) assert_equal(OpenSSL::ASN1::Set, dn.value[2].class) assert_equal(1, dn.value[0].value.size) assert_equal(1, dn.value[1].value.size) assert_equal(1, dn.value[2].value.size) assert_equal(OpenSSL::ASN1::Sequence, dn.value[0].value[0].class) assert_equal(OpenSSL::ASN1::Sequence, dn.value[1].value[0].class) assert_equal(OpenSSL::ASN1::Sequence, dn.value[2].value[0].class) assert_equal(2, dn.value[0].value[0].value.size) assert_equal(2, dn.value[1].value[0].value.size) assert_equal(2, dn.value[2].value[0].value.size) oid, value = *dn.value[0].value[0].value assert_equal(OpenSSL::ASN1::ObjectId, oid.class) assert_equal("0.9.2342.19200300.100.1.25", oid.oid) assert_equal(OpenSSL::ASN1::IA5String, value.class) assert_equal("org", value.value) oid, value = *dn.value[1].value[0].value assert_equal(OpenSSL::ASN1::ObjectId, oid.class) assert_equal("0.9.2342.19200300.100.1.25", oid.oid) assert_equal(OpenSSL::ASN1::IA5String, value.class) assert_equal("ruby-lang", value.value) oid, value = *dn.value[2].value[0].value assert_equal(OpenSSL::ASN1::ObjectId, oid.class) assert_equal("2.5.4.3", oid.oid) assert_equal(OpenSSL::ASN1::UTF8String, value.class) assert_equal("TestCA", value.value) validity = tbs_cert.value[4] assert_equal(OpenSSL::ASN1::Sequence, validity.class) assert_equal(2, validity.value.size) assert_equal(OpenSSL::ASN1::UTCTime, validity.value[0].class) assert_equal(now, validity.value[0].value) assert_equal(OpenSSL::ASN1::UTCTime, validity.value[1].class) assert_equal(now+3600, validity.value[1].value) dn = tbs_cert.value[5] # subject assert_equal(subj.hash, OpenSSL::X509::Name.new(dn).hash) assert_equal(OpenSSL::ASN1::Sequence, dn.class) assert_equal(3, dn.value.size) assert_equal(OpenSSL::ASN1::Set, dn.value[0].class) assert_equal(OpenSSL::ASN1::Set, dn.value[1].class) assert_equal(OpenSSL::ASN1::Set, dn.value[2].class) assert_equal(1, dn.value[0].value.size) assert_equal(1, dn.value[1].value.size) assert_equal(1, dn.value[2].value.size) assert_equal(OpenSSL::ASN1::Sequence, dn.value[0].value[0].class) assert_equal(OpenSSL::ASN1::Sequence, dn.value[1].value[0].class) assert_equal(OpenSSL::ASN1::Sequence, dn.value[2].value[0].class) assert_equal(2, dn.value[0].value[0].value.size) assert_equal(2, dn.value[1].value[0].value.size) assert_equal(2, dn.value[2].value[0].value.size) oid, value = *dn.value[0].value[0].value assert_equal(OpenSSL::ASN1::ObjectId, oid.class) assert_equal("0.9.2342.19200300.100.1.25", oid.oid) assert_equal(OpenSSL::ASN1::IA5String, value.class) assert_equal("org", value.value) oid, value = *dn.value[1].value[0].value assert_equal(OpenSSL::ASN1::ObjectId, oid.class) assert_equal("0.9.2342.19200300.100.1.25", oid.oid) assert_equal(OpenSSL::ASN1::IA5String, value.class) assert_equal("ruby-lang", value.value) oid, value = *dn.value[2].value[0].value assert_equal(OpenSSL::ASN1::ObjectId, oid.class) assert_equal("2.5.4.3", oid.oid) assert_equal(OpenSSL::ASN1::UTF8String, value.class) assert_equal("TestCA", value.value) pkey = tbs_cert.value[6] assert_equal(OpenSSL::ASN1::Sequence, pkey.class) assert_equal(2, pkey.value.size) assert_equal(OpenSSL::ASN1::Sequence, pkey.value[0].class) assert_equal(2, pkey.value[0].value.size) assert_equal(OpenSSL::ASN1::ObjectId, pkey.value[0].value[0].class) assert_equal("1.2.840.113549.1.1.1", pkey.value[0].value[0].oid) assert_equal(OpenSSL::ASN1::BitString, pkey.value[1].class) assert_equal(0, pkey.value[1].unused_bits) spkey = OpenSSL::ASN1.decode(pkey.value[1].value) assert_equal(OpenSSL::ASN1::Sequence, spkey.class) assert_equal(2, spkey.value.size) assert_equal(OpenSSL::ASN1::Integer, spkey.value[0].class) assert_equal(143085709396403084580358323862163416700436550432664688288860593156058579474547937626086626045206357324274536445865308750491138538454154232826011964045825759324933943290377903384882276841880081931690695505836279972214003660451338124170055999155993192881685495391496854691199517389593073052473319331505702779271, spkey.value[0].value) assert_equal(OpenSSL::ASN1::Integer, spkey.value[1].class) assert_equal(65537, spkey.value[1].value) extensions = tbs_cert.value[7] assert_equal(:CONTEXT_SPECIFIC, extensions.tag_class) assert_equal(3, extensions.tag) assert_equal(1, extensions.value.size) assert_equal(OpenSSL::ASN1::Sequence, extensions.value[0].class) assert_equal(3, extensions.value[0].value.size) ext = extensions.value[0].value[0] # basicConstraints assert_equal(OpenSSL::ASN1::Sequence, ext.class) assert_equal(3, ext.value.size) assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class) assert_equal("2.5.29.19", ext.value[0].oid) assert_equal(OpenSSL::ASN1::Boolean, ext.value[1].class) assert_equal(true, ext.value[1].value) assert_equal(OpenSSL::ASN1::OctetString, ext.value[2].class) extv = OpenSSL::ASN1.decode(ext.value[2].value) assert_equal(OpenSSL::ASN1::Sequence, extv.class) assert_equal(2, extv.value.size) assert_equal(OpenSSL::ASN1::Boolean, extv.value[0].class) assert_equal(true, extv.value[0].value) assert_equal(OpenSSL::ASN1::Integer, extv.value[1].class) assert_equal(1, extv.value[1].value) ext = extensions.value[0].value[1] # keyUsage assert_equal(OpenSSL::ASN1::Sequence, ext.class) assert_equal(3, ext.value.size) assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class) assert_equal("2.5.29.15", ext.value[0].oid) assert_equal(OpenSSL::ASN1::Boolean, ext.value[1].class) assert_equal(true, ext.value[1].value) assert_equal(OpenSSL::ASN1::OctetString, ext.value[2].class) extv = OpenSSL::ASN1.decode(ext.value[2].value) assert_equal(OpenSSL::ASN1::BitString, extv.class) str = "\000"; str[0] = 0b00000110.chr assert_equal(str, extv.value) ext = extensions.value[0].value[2] # subjetKeyIdentifier assert_equal(OpenSSL::ASN1::Sequence, ext.class) assert_equal(2, ext.value.size) assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class) assert_equal("2.5.29.14", ext.value[0].oid) assert_equal(OpenSSL::ASN1::OctetString, ext.value[1].class) extv = OpenSSL::ASN1.decode(ext.value[1].value) assert_equal(OpenSSL::ASN1::OctetString, extv.class) sha1 = OpenSSL::Digest::SHA1.new sha1.update(pkey.value[1].value) assert_equal(sha1.digest, extv.value) assert_equal(OpenSSL::ASN1::Sequence, sig_alg.class) assert_equal(2, sig_alg.value.size) assert_equal(OpenSSL::ASN1::ObjectId, pkey.value[0].value[0].class) assert_equal("1.2.840.113549.1.1.1", pkey.value[0].value[0].oid) assert_equal(OpenSSL::ASN1::Null, pkey.value[0].value[1].class) assert_equal(OpenSSL::ASN1::BitString, sig_val.class) cululated_sig = key.sign(OpenSSL::Digest::SHA1.new, tbs_cert.to_der) assert_equal(cululated_sig, sig_val.value) end def test_encode_boolean encode_decode_test(OpenSSL::ASN1::Boolean, [true, false]) end def test_encode_integer encode_decode_test(OpenSSL::ASN1::Integer, [72, -127, -128, 128, -1, 0, 1, -(2**12345), 2**12345]) end def test_encode_nil m = OpenSSL::ASN1 [ m::Boolean, m::Integer, m::BitString, m::OctetString, m::ObjectId, m::Enumerated, m::UTF8String, m::UTCTime, m::GeneralizedTime, m::Sequence, m::Set ].each do |klass| #Primitives raise TypeError, Constructives NoMethodError assert_raise(TypeError, NoMethodError) { klass.send(:new, nil).to_der } end end def encode_decode_test(type, values) values.each do |v| assert_equal(v, OpenSSL::ASN1.decode(type.new(v).to_der).value) end end def test_decode_pem #should fail gracefully (cf. [ruby-dev:44542]) pem = <<-_EOS_ -----BEGIN CERTIFICATE----- MIIC8zCCAdugAwIBAgIBATANBgkqhkiG9w0BAQUFADA9MRMwEQYKCZImiZPyLGQB GRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMQswCQYDVQQDDAJDQTAe Fw0xMTA5MjUxMzQ4MjZaFw0xMTA5MjUxNDQ4MjZaMD0xEzARBgoJkiaJk/IsZAEZ FgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMMAkNBMIIB IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuV9ht9J7k4NBs38jOXvvTKY9 gW8nLICSno5EETR1cuF7i4pNs9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enen fzq/t/e/1IRW0wkJUJUFQign4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWm qbjs07JbuS4QQGGXLc+Su96DkYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v6 8JkRFIhdGlb6JL8fllf/A/blNwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX 9KZYcU00mOX+fdxOSnGqS/8JDRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wID AQABMA0GCSqGSIb3DQEBBQUAA4IBAQAiAtrIr1pLX4GYN5klviWKb8HC9ICYuAFI NfE3FwqzErEVXotuMe3yPVyB3Bv6rjYY/x5EtS5+WPTbHlvHZTkfcsnTpizcn4mW dJ6dDRaFCHt1YKKjUxqBt9lvvrc3nReYZN/P+s1mrDhWzGf8iPZgf8sFUHgnaK7W CXRVXmPFgCDRNpDDVQ0MQkr509yYfTH+dujNzqTCwSvkyZFyQ7Oe8Yj0VR6kquG3 rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm /93PnPG1IvPjYNd5VlV+sXSnaxQn974HRCsMv7jA8BD6IgSaX6WK -----END CERTIFICATE----- _EOS_ assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.decode(pem) } assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.decode_all(pem) } end def test_primitive_cannot_set_infinite_length prim = OpenSSL::ASN1::Integer.new(50) assert_equal false, prim.infinite_length assert_not_respond_to prim, :infinite_length= end def test_decode_all expected = %w{ 02 01 01 02 01 02 02 01 03 } raw = [expected.join('')].pack('H*') ary = OpenSSL::ASN1.decode_all(raw) assert_equal(3, ary.size) ary.each_with_index do |asn1, i| assert_universal(OpenSSL::ASN1::INTEGER, asn1) assert_equal(i + 1, asn1.value) end end def test_decode_utctime expected = Time.at 1374535380 assert_equal expected, OpenSSL::ASN1.decode("\x17\v1307222323Z").value expected += 17 assert_equal expected, OpenSSL::ASN1.decode("\x17\r130722232317Z").value end def test_encode_utctime_2k38 encoded = OpenSSL::ASN1::UTCTime(2 ** 31 - 1).to_der assert_equal 2 ** 31 - 1, OpenSSL::ASN1.decode(encoded).value.to_i encoded = OpenSSL::ASN1::UTCTime(2 ** 31).to_der assert_equal 2 ** 31, OpenSSL::ASN1.decode(encoded).value.to_i end def test_decode_generalisedtime expected = Time.at 1481225640 assert_equal expected, OpenSSL::ASN1.decode("\x18\x0D201612081934Z").value expected += 29 assert_equal expected, OpenSSL::ASN1.decode("\x18\x0F20161208193429Z").value end def test_decode_enumerated encoded = OpenSSL::ASN1.Enumerated(0).to_der assert_equal "\x0a\x01\x00".b, encoded assert_equal encoded, OpenSSL::ASN1.decode(encoded).to_der end def test_create_inf_length_primitive expected = %w{ 24 80 04 01 61 00 00 } raw = [expected.join('')].pack('H*') content = [OpenSSL::ASN1::OctetString.new("a"), OpenSSL::ASN1::EndOfContent.new] cons = OpenSSL::ASN1::Constructive.new(content, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) cons.infinite_length = true assert_equal(nil, cons.tagging) assert_equal(raw, cons.to_der) asn1 = OpenSSL::ASN1.decode(raw) assert(asn1.infinite_length) assert_equal(raw, asn1.to_der) end def test_cons_without_inf_length_forbidden assert_raise(OpenSSL::ASN1::ASN1Error) do val = OpenSSL::ASN1::OctetString.new('a') cons = OpenSSL::ASN1::Constructive.new([val], OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) cons.to_der end end def test_cons_without_array_forbidden assert_raise(OpenSSL::ASN1::ASN1Error) do val = OpenSSL::ASN1::OctetString.new('a') cons = OpenSSL::ASN1::Constructive.new(val, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) cons.infinite_length = true cons.to_der end end def test_parse_empty_sequence expected = %w{ A0 07 30 02 30 00 02 01 00 } raw = [expected.join('')].pack('H*') asn1 = OpenSSL::ASN1.decode(raw) assert_equal(raw, asn1.to_der) assert_equal(2, asn1.value.size) seq = asn1.value[0] assert_equal(1, seq.value.size) inner_seq = seq.value[0] assert_equal(0, inner_seq.value.size) end def test_parse_tagged_0_infinite expected = %w{ 30 80 02 01 01 80 01 02 00 00 } raw = [expected.join('')].pack('H*') asn1 = OpenSSL::ASN1.decode(raw) assert_equal(3, asn1.value.size) int = asn1.value[0] assert_universal(OpenSSL::ASN1::INTEGER, int) tagged = asn1.value[1] assert_equal(0, tagged.tag) assert_universal(OpenSSL::ASN1::EOC, asn1.value[2]) assert_equal(raw, asn1.to_der) end def test_seq_infinite_length content = [ OpenSSL::ASN1::Null.new(nil), OpenSSL::ASN1::EndOfContent.new ] cons = OpenSSL::ASN1::Sequence.new(content) cons.infinite_length = true expected = %w{ 30 80 05 00 00 00 } raw = [expected.join('')].pack('H*') assert_equal(raw, cons.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_set_infinite_length content = [ OpenSSL::ASN1::Null.new(nil), OpenSSL::ASN1::EndOfContent.new() ] cons = OpenSSL::ASN1::Set.new(content) cons.infinite_length = true expected = %w{ 31 80 05 00 00 00 } raw = [expected.join('')].pack('H*') assert_equal(raw, cons.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_octet_string_infinite_length octets = [ OpenSSL::ASN1::OctetString.new('aaa'), OpenSSL::ASN1::EndOfContent.new() ] cons = OpenSSL::ASN1::Constructive.new(octets, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) cons.infinite_length = true expected = %w{ 24 80 04 03 61 61 61 00 00 } raw = [expected.join('')].pack('H*') assert_equal(raw, cons.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_prim_explicit_tagging oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) expected = %w{ A0 03 04 01 61 } raw = [expected.join('')].pack('H*') assert_equal(raw, oct_str.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_prim_explicit_tagging_tag_class oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) oct_str2 = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT, :CONTEXT_SPECIFIC) assert_equal(oct_str.to_der, oct_str2.to_der) end def test_prim_implicit_tagging int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) expected = %w{ 80 01 01 } raw = [expected.join('')].pack('H*') assert_equal(raw, int.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_prim_implicit_tagging_tag_class int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) int2 = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT, :CONTEXT_SPECIFIC); assert_equal(int.to_der, int2.to_der) end def test_cons_explicit_tagging content = [ OpenSSL::ASN1::PrintableString.new('abc') ] seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT) expected = %w{ A2 07 30 05 13 03 61 62 63 } raw = [expected.join('')].pack('H*') assert_equal(raw, seq.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_cons_explicit_tagging_inf_length content = [ OpenSSL::ASN1::PrintableString.new('abc') , OpenSSL::ASN1::EndOfContent.new() ] seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT) seq.infinite_length = true expected = %w{ A2 80 30 80 13 03 61 62 63 00 00 00 00 } raw = [expected.join('')].pack('H*') assert_equal(raw, seq.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_cons_implicit_tagging content = [ OpenSSL::ASN1::Null.new(nil) ] seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT) expected = %w{ A1 02 05 00 } raw = [expected.join('')].pack('H*') assert_equal(raw, seq.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_cons_implicit_tagging_inf_length content = [ OpenSSL::ASN1::Null.new(nil), OpenSSL::ASN1::EndOfContent.new() ] seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT) seq.infinite_length = true expected = %w{ A1 80 05 00 00 00 } raw = [expected.join('')].pack('H*') assert_equal(raw, seq.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_octet_string_infinite_length_explicit_tagging octets = [ OpenSSL::ASN1::OctetString.new('aaa'), OpenSSL::ASN1::EndOfContent.new() ] cons = OpenSSL::ASN1::Constructive.new(octets, 1, :EXPLICIT) cons.infinite_length = true expected = %w{ A1 80 24 80 04 03 61 61 61 00 00 00 00 } raw = [expected.join('')].pack('H*') assert_equal(raw, cons.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_octet_string_infinite_length_implicit_tagging octets = [ OpenSSL::ASN1::OctetString.new('aaa'), OpenSSL::ASN1::EndOfContent.new() ] cons = OpenSSL::ASN1::Constructive.new(octets, 0, :IMPLICIT) cons.infinite_length = true expected = %w{ A0 80 04 03 61 61 61 00 00 } raw = [expected.join('')].pack('H*') assert_equal(raw, cons.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_recursive_octet_string_infinite_length octets_sub1 = [ OpenSSL::ASN1::OctetString.new("\x01"), OpenSSL::ASN1::EndOfContent.new() ] octets_sub2 = [ OpenSSL::ASN1::OctetString.new("\x02"), OpenSSL::ASN1::EndOfContent.new() ] container1 = OpenSSL::ASN1::Constructive.new(octets_sub1, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) container1.infinite_length = true container2 = OpenSSL::ASN1::Constructive.new(octets_sub2, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) container2.infinite_length = true octets3 = OpenSSL::ASN1::OctetString.new("\x03") octets = [ container1, container2, octets3, OpenSSL::ASN1::EndOfContent.new() ] cons = OpenSSL::ASN1::Constructive.new(octets, OpenSSL::ASN1::OCTET_STRING, nil, :UNIVERSAL) cons.infinite_length = true expected = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 } raw = [expected.join('')].pack('H*') assert_equal(raw, cons.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_bit_string_infinite_length content = [ OpenSSL::ASN1::BitString.new("\x01"), OpenSSL::ASN1::EndOfContent.new() ] cons = OpenSSL::ASN1::Constructive.new(content, OpenSSL::ASN1::BIT_STRING, nil, :UNIVERSAL) cons.infinite_length = true expected = %w{ 23 80 03 02 00 01 00 00 } raw = [expected.join('')].pack('H*') assert_equal(raw, cons.to_der) assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) end def test_primitive_inf_length assert_raise(OpenSSL::ASN1::ASN1Error) do spec = %w{ 02 80 02 01 01 00 00 } raw = [spec.join('')].pack('H*') OpenSSL::ASN1.decode(raw) OpenSSL::ASN1.decode_all(raw) end end def test_recursive_octet_string_parse test = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 } raw = [test.join('')].pack('H*') asn1 = OpenSSL::ASN1.decode(raw) assert_equal(OpenSSL::ASN1::Constructive, asn1.class) assert_universal(OpenSSL::ASN1::OCTET_STRING, asn1) assert_equal(true, asn1.infinite_length) assert_equal(4, asn1.value.size) nested1 = asn1.value[0] assert_equal(OpenSSL::ASN1::Constructive, nested1.class) assert_universal(OpenSSL::ASN1::OCTET_STRING, nested1) assert_equal(true, nested1.infinite_length) assert_equal(2, nested1.value.size) oct1 = nested1.value[0] assert_universal(OpenSSL::ASN1::OCTET_STRING, oct1) assert_equal(false, oct1.infinite_length) assert_universal(OpenSSL::ASN1::EOC, nested1.value[1]) assert_equal(false, nested1.value[1].infinite_length) nested2 = asn1.value[1] assert_equal(OpenSSL::ASN1::Constructive, nested2.class) assert_universal(OpenSSL::ASN1::OCTET_STRING, nested2) assert_equal(true, nested2.infinite_length) assert_equal(2, nested2.value.size) oct2 = nested2.value[0] assert_universal(OpenSSL::ASN1::OCTET_STRING, oct2) assert_equal(false, oct2.infinite_length) assert_universal(OpenSSL::ASN1::EOC, nested2.value[1]) assert_equal(false, nested2.value[1].infinite_length) oct3 = asn1.value[2] assert_universal(OpenSSL::ASN1::OCTET_STRING, oct3) assert_equal(false, oct3.infinite_length) assert_universal(OpenSSL::ASN1::EOC, asn1.value[3]) assert_equal(false, asn1.value[3].infinite_length) end def test_decode_constructed_overread test = %w{ 31 06 31 02 30 02 05 00 } # ^ <- invalid raw = [test.join].pack("H*") ret = [] assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.traverse(raw) { |x| ret << x } } assert_equal 2, ret.size assert_equal 17, ret[0][6] assert_equal 17, ret[1][6] test = %w{ 31 80 30 03 00 00 } # ^ <- invalid raw = [test.join].pack("H*") ret = [] assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1.traverse(raw) { |x| ret << x } } assert_equal 1, ret.size assert_equal 17, ret[0][6] end def test_constructive_each data = [OpenSSL::ASN1::Integer.new(0), OpenSSL::ASN1::Integer.new(1)] seq = OpenSSL::ASN1::Sequence.new data assert_equal data, seq.entries end private def assert_universal(tag, asn1) assert_equal(tag, asn1.tag) if asn1.respond_to?(:tagging) assert_nil(asn1.tagging) end assert_equal(:UNIVERSAL, asn1.tag_class) end end end openssl-2.0.9/test/test_bn.rb000066400000000000000000000041561336157045000161610ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestBN < OpenSSL::TestCase def test_new_str e1 = OpenSSL::BN.new(999.to_s(16), 16) # OpenSSL::BN.new(str, 16) must be most stable e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16) assert_equal(e1, OpenSSL::BN.new("999")) assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s)) assert_equal(e1, OpenSSL::BN.new("999", 10)) assert_equal(e2, OpenSSL::BN.new((2**107-1).to_s, 10)) assert_equal(e1, OpenSSL::BN.new("\x03\xE7", 2)) assert_equal(e2, OpenSSL::BN.new("\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 2)) assert_equal(e1, OpenSSL::BN.new("\x00\x00\x00\x02\x03\xE7", 0)) assert_equal(e2, OpenSSL::BN.new("\x00\x00\x00\x0E\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 0)) end def test_new_bn e1 = OpenSSL::BN.new(999.to_s(16), 16) e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16) assert_equal(e1, OpenSSL::BN.new(e1)) assert_equal(e2, OpenSSL::BN.new(e2)) end def test_new_integer assert_equal(999.to_bn, OpenSSL::BN.new(999)) assert_equal((2 ** 107 - 1).to_bn, OpenSSL::BN.new(2 ** 107 - 1)) assert_equal(-999.to_bn, OpenSSL::BN.new(-999)) assert_equal((-(2 ** 107 - 1)).to_bn, OpenSSL::BN.new(-(2 ** 107 - 1))) end def test_to_bn e1 = OpenSSL::BN.new(999.to_s(16), 16) e2 = OpenSSL::BN.new((2**107-1).to_s(16), 16) assert_equal(e1, 999.to_bn) assert_equal(e2, (2**107-1).to_bn) end def test_prime_p assert_equal(true, OpenSSL::BN.new((2 ** 107 - 1).to_s(16), 16).prime?) assert_equal(true, OpenSSL::BN.new((2 ** 127 - 1).to_s(16), 16).prime?(1)) end def test_cmp bn1 = OpenSSL::BN.new('1') bn2 = OpenSSL::BN.new('1') bn3 = OpenSSL::BN.new('2') assert_equal(false, bn1 == nil) assert_equal(true, bn1 != nil) assert_equal(true, bn1 == bn2) assert_equal(false, bn1 == bn3) assert_equal(true, bn1.eql?(bn2)) assert_equal(false, bn1.eql?(bn3)) assert_equal(bn1.hash, bn2.hash) assert_not_equal(bn3.hash, bn1.hash) assert_instance_of(String, bn1.hash.to_s) end end end openssl-2.0.9/test/test_buffering.rb000066400000000000000000000025621336157045000175300ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestBuffering < OpenSSL::TestCase class IO include OpenSSL::Buffering attr_accessor :sync def initialize @io = "" def @io.sync true end super @sync = false end def string @io end def sysread(size) str = @io.slice!(0, size) raise EOFError if str.empty? str end def syswrite(str) @io << str str.size end end def setup super @io = IO.new end def test_flush @io.write 'a' assert_not_predicate @io, :sync assert_empty @io.string assert_equal @io, @io.flush assert_not_predicate @io, :sync assert_equal 'a', @io.string end def test_flush_error @io.write 'a' assert_not_predicate @io, :sync assert_empty @io.string def @io.syswrite *a raise SystemCallError, 'fail' end assert_raise SystemCallError do @io.flush end assert_not_predicate @io, :sync, 'sync must not change' end def test_getc @io.syswrite('abc') assert_equal(?a, @io.getc) assert_equal(?b, @io.getc) assert_equal(?c, @io.getc) end def test_each_byte @io.syswrite('abc') res = [] @io.each_byte do |c| res << c end assert_equal([97, 98, 99], res) end end end openssl-2.0.9/test/test_cipher.rb000066400000000000000000000267261336157045000170430ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestCipher < OpenSSL::TestCase module Helper def has_cipher?(name) @ciphers ||= OpenSSL::Cipher.ciphers @ciphers.include?(name) end end include Helper extend Helper def test_encrypt_decrypt # NIST SP 800-38A F.2.1 key = ["2b7e151628aed2a6abf7158809cf4f3c"].pack("H*") iv = ["000102030405060708090a0b0c0d0e0f"].pack("H*") pt = ["6bc1bee22e409f96e93d7e117393172a" \ "ae2d8a571e03ac9c9eb76fac45af8e51"].pack("H*") ct = ["7649abac8119b246cee98e9b12e9197d" \ "5086cb9b507219ee95db113a917678b2"].pack("H*") cipher = new_encryptor("aes-128-cbc", key: key, iv: iv, padding: 0) assert_equal ct, cipher.update(pt) << cipher.final cipher = new_decryptor("aes-128-cbc", key: key, iv: iv, padding: 0) assert_equal pt, cipher.update(ct) << cipher.final end def test_pkcs5_keyivgen pass = "\x00" * 8 salt = "\x01" * 8 num = 2048 pt = "data to be encrypted" cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt cipher.pkcs5_keyivgen(pass, salt, num, "MD5") s1 = cipher.update(pt) << cipher.final d1 = num.times.inject(pass + salt) {|out, _| OpenSSL::Digest::MD5.digest(out) } d2 = num.times.inject(d1 + pass + salt) {|out, _| OpenSSL::Digest::MD5.digest(out) } key = (d1 + d2)[0, 24] iv = (d1 + d2)[24, 8] cipher = new_encryptor("DES-EDE3-CBC", key: key, iv: iv) s2 = cipher.update(pt) << cipher.final assert_equal s1, s2 cipher2 = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt assert_raise(ArgumentError) { cipher2.pkcs5_keyivgen(pass, salt, -1, "MD5") } end def test_info cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt assert_equal "DES-EDE3-CBC", cipher.name assert_equal 24, cipher.key_len assert_equal 8, cipher.iv_len end def test_dup cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt assert_equal cipher.name, cipher.dup.name cipher.encrypt cipher.random_key cipher.random_iv tmpc = cipher.dup s1 = cipher.update("data") + cipher.final s2 = tmpc.update("data") + tmpc.final assert_equal(s1, s2, "encrypt dup") end def test_reset cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt cipher.encrypt cipher.random_key cipher.random_iv s1 = cipher.update("data") + cipher.final cipher.reset s2 = cipher.update("data") + cipher.final assert_equal(s1, s2, "encrypt reset") end def test_key_iv_set cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt assert_raise(ArgumentError) { cipher.key = "\x01" * 23 } assert_nothing_raised { cipher.key = "\x01" * 24 } assert_raise(ArgumentError) { cipher.key = "\x01" * 25 } assert_raise(ArgumentError) { cipher.iv = "\x01" * 7 } assert_nothing_raised { cipher.iv = "\x01" * 8 } assert_raise(ArgumentError) { cipher.iv = "\x01" * 9 } end def test_random_key_iv data = "data" s1, s2 = 2.times.map do cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt cipher.random_key cipher.iv = "\x01" * 16 cipher.update(data) << cipher.final end assert_not_equal s1, s2 s1, s2 = 2.times.map do cipher = OpenSSL::Cipher.new("aes-128-cbc").encrypt cipher.key = "\x01" * 16 cipher.random_iv cipher.update(data) << cipher.final end assert_not_equal s1, s2 end def test_empty_data cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt cipher.random_key assert_raise(ArgumentError) { cipher.update("") } end def test_initialize cipher = OpenSSL::Cipher.new("DES-EDE3-CBC") assert_raise(RuntimeError) { cipher.__send__(:initialize, "DES-EDE3-CBC") } assert_raise(RuntimeError) { OpenSSL::Cipher.allocate.final } end def test_ctr_if_exists # NIST SP 800-38A F.5.1 key = ["2b7e151628aed2a6abf7158809cf4f3c"].pack("H*") iv = ["f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"].pack("H*") pt = ["6bc1bee22e409f96e93d7e117393172a" \ "ae2d8a571e03ac9c9eb76fac45af8e51"].pack("H*") ct = ["874d6191b620e3261bef6864990db6ce" \ "9806f66b7970fdff8617187bb9fffdff"].pack("H*") cipher = new_encryptor("aes-128-ctr", key: key, iv: iv, padding: 0) assert_equal ct, cipher.update(pt) << cipher.final cipher = new_decryptor("aes-128-ctr", key: key, iv: iv, padding: 0) assert_equal pt, cipher.update(ct) << cipher.final end if has_cipher?('aes-128-ctr') def test_ciphers OpenSSL::Cipher.ciphers.each{|name| next if /netbsd/ =~ RUBY_PLATFORM && /idea|rc5/i =~ name begin assert_kind_of(OpenSSL::Cipher, OpenSSL::Cipher.new(name)) rescue OpenSSL::Cipher::CipherError => e raise unless /wrap/ =~ name and /wrap mode not allowed/ =~ e.message end } end def test_AES pt = File.read(__FILE__) %w(ECB CBC CFB OFB).each{|mode| c1 = OpenSSL::Cipher::AES256.new(mode) c1.encrypt c1.pkcs5_keyivgen("passwd") ct = c1.update(pt) + c1.final c2 = OpenSSL::Cipher::AES256.new(mode) c2.decrypt c2.pkcs5_keyivgen("passwd") assert_equal(pt, c2.update(ct) + c2.final) } end def test_update_raise_if_key_not_set assert_raise(OpenSSL::Cipher::CipherError) do # it caused OpenSSL SEGV by uninitialized key [Bug #2768] OpenSSL::Cipher::AES128.new("ECB").update "." * 17 end end def test_authenticated if has_cipher?('aes-128-gcm') cipher = OpenSSL::Cipher.new('aes-128-gcm') assert_predicate(cipher, :authenticated?) end cipher = OpenSSL::Cipher.new('aes-128-cbc') assert_not_predicate(cipher, :authenticated?) end def test_aes_gcm # GCM spec Appendix B Test Case 4 key = ["feffe9928665731c6d6a8f9467308308"].pack("H*") iv = ["cafebabefacedbaddecaf888"].pack("H*") aad = ["feedfacedeadbeeffeedfacedeadbeef" \ "abaddad2"].pack("H*") pt = ["d9313225f88406e5a55909c5aff5269a" \ "86a7a9531534f7da2e4c303d8a318a72" \ "1c3c0c95956809532fcf0e2449a6b525" \ "b16aedf5aa0de657ba637b39"].pack("H*") ct = ["42831ec2217774244b7221b784d0d49c" \ "e3aa212f2c02a4e035c17e2329aca12e" \ "21d514b25466931c7d8f6a5aac84aa05" \ "1ba30b396a0aac973d58e091"].pack("H*") tag = ["5bc94fbc3221a5db94fae95ae7121a47"].pack("H*") cipher = new_encryptor("aes-128-gcm", key: key, iv: iv, auth_data: aad) assert_equal ct, cipher.update(pt) << cipher.final assert_equal tag, cipher.auth_tag cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag, auth_data: aad) assert_equal pt, cipher.update(ct) << cipher.final # truncated tag is accepted cipher = new_encryptor("aes-128-gcm", key: key, iv: iv, auth_data: aad) assert_equal ct, cipher.update(pt) << cipher.final assert_equal tag[0, 8], cipher.auth_tag(8) cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag[0, 8], auth_data: aad) assert_equal pt, cipher.update(ct) << cipher.final # wrong tag is rejected tag2 = tag.dup tag2.setbyte(-1, (tag2.getbyte(-1) + 1) & 0xff) cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag2, auth_data: aad) cipher.update(ct) assert_raise(OpenSSL::Cipher::CipherError) { cipher.final } # wrong aad is rejected aad2 = aad[0..-2] << aad[-1].succ cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag, auth_data: aad2) cipher.update(ct) assert_raise(OpenSSL::Cipher::CipherError) { cipher.final } # wrong ciphertext is rejected ct2 = ct[0..-2] << ct[-1].succ cipher = new_decryptor("aes-128-gcm", key: key, iv: iv, auth_tag: tag, auth_data: aad) cipher.update(ct2) assert_raise(OpenSSL::Cipher::CipherError) { cipher.final } end if has_cipher?("aes-128-gcm") def test_aes_gcm_variable_iv_len # GCM spec Appendix B Test Case 5 key = ["feffe9928665731c6d6a8f9467308308"].pack("H*") iv = ["cafebabefacedbad"].pack("H*") aad = ["feedfacedeadbeeffeedfacedeadbeef" \ "abaddad2"].pack("H*") pt = ["d9313225f88406e5a55909c5aff5269a" \ "86a7a9531534f7da2e4c303d8a318a72" \ "1c3c0c95956809532fcf0e2449a6b525" \ "b16aedf5aa0de657ba637b39"].pack("H*") ct = ["61353b4c2806934a777ff51fa22a4755" \ "699b2a714fcdc6f83766e5f97b6c7423" \ "73806900e49f24b22b097544d4896b42" \ "4989b5e1ebac0f07c23f4598"].pack("H*") tag = ["3612d2e79e3b0785561be14aaca2fccb"].pack("H*") cipher = new_encryptor("aes-128-gcm", key: key, iv_len: 8, iv: iv, auth_data: aad) assert_equal ct, cipher.update(pt) << cipher.final assert_equal tag, cipher.auth_tag cipher = new_decryptor("aes-128-gcm", key: key, iv_len: 8, iv: iv, auth_tag: tag, auth_data: aad) assert_equal pt, cipher.update(ct) << cipher.final end if has_cipher?("aes-128-gcm") def test_aes_ocb_tag_len # RFC 7253 Appendix A; the second sample key = ["000102030405060708090A0B0C0D0E0F"].pack("H*") iv = ["BBAA99887766554433221101"].pack("H*") aad = ["0001020304050607"].pack("H*") pt = ["0001020304050607"].pack("H*") ct = ["6820B3657B6F615A"].pack("H*") tag = ["5725BDA0D3B4EB3A257C9AF1F8F03009"].pack("H*") cipher = new_encryptor("aes-128-ocb", key: key, iv: iv, auth_data: aad) assert_equal ct, cipher.update(pt) << cipher.final assert_equal tag, cipher.auth_tag cipher = new_decryptor("aes-128-ocb", key: key, iv: iv, auth_tag: tag, auth_data: aad) assert_equal pt, cipher.update(ct) << cipher.final # RFC 7253 Appendix A; with 96 bits tag length key = ["0F0E0D0C0B0A09080706050403020100"].pack("H*") iv = ["BBAA9988776655443322110D"].pack("H*") aad = ["000102030405060708090A0B0C0D0E0F1011121314151617" \ "18191A1B1C1D1E1F2021222324252627"].pack("H*") pt = ["000102030405060708090A0B0C0D0E0F1011121314151617" \ "18191A1B1C1D1E1F2021222324252627"].pack("H*") ct = ["1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1" \ "A0124B0A55BAE884ED93481529C76B6A"].pack("H*") tag = ["D0C515F4D1CDD4FDAC4F02AA"].pack("H*") cipher = new_encryptor("aes-128-ocb", auth_tag_len: 12, key: key, iv: iv, auth_data: aad) assert_equal ct, cipher.update(pt) << cipher.final assert_equal tag, cipher.auth_tag cipher = new_decryptor("aes-128-ocb", auth_tag_len: 12, key: key, iv: iv, auth_tag: tag, auth_data: aad) assert_equal pt, cipher.update(ct) << cipher.final end if has_cipher?("aes-128-ocb") def test_aes_gcm_key_iv_order_issue pt = "[ruby/openssl#49]" cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt cipher.key = "x" * 16 cipher.iv = "a" * 12 ct1 = cipher.update(pt) << cipher.final tag1 = cipher.auth_tag cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt cipher.iv = "a" * 12 cipher.key = "x" * 16 ct2 = cipher.update(pt) << cipher.final tag2 = cipher.auth_tag assert_equal ct1, ct2 assert_equal tag1, tag2 end if has_cipher?("aes-128-gcm") def test_non_aead_cipher_set_auth_data assert_raise(OpenSSL::Cipher::CipherError) { cipher = OpenSSL::Cipher.new("aes-128-cfb").encrypt cipher.auth_data = "123" } end if has_cipher?("aes-128-gcm") private def new_encryptor(algo, **kwargs) OpenSSL::Cipher.new(algo).tap do |cipher| cipher.encrypt kwargs.each {|k, v| cipher.send(:"#{k}=", v) } end end def new_decryptor(algo, **kwargs) OpenSSL::Cipher.new(algo).tap do |cipher| cipher.decrypt kwargs.each {|k, v| cipher.send(:"#{k}=", v) } end end end end openssl-2.0.9/test/test_config.rb000066400000000000000000000223661336157045000170320ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestConfig < OpenSSL::TestCase def setup super file = Tempfile.open("openssl.cnf") file << <<__EOD__ HOME = . [ ca ] default_ca = CA_default [ CA_default ] dir = ./demoCA certs = ./certs __EOD__ file.close @tmpfile = file @it = OpenSSL::Config.new(file.path) end def teardown super @tmpfile.close! end def test_constants assert(defined?(OpenSSL::Config::DEFAULT_CONFIG_FILE)) config_file = OpenSSL::Config::DEFAULT_CONFIG_FILE pend "DEFAULT_CONFIG_FILE may return a wrong path on your platforms. [Bug #6830]" unless File.readable?(config_file) assert_nothing_raised do OpenSSL::Config.load(config_file) end end def test_s_parse c = OpenSSL::Config.parse('') assert_equal("[ default ]\n\n", c.to_s) c = OpenSSL::Config.parse(@it.to_s) assert_equal(['CA_default', 'ca', 'default'], c.sections.sort) end def test_s_parse_format c = OpenSSL::Config.parse(<<__EOC__) baz =qx\t # "baz = qx" foo::bar = baz # shortcut section::key format default::bar = baz # ditto a=\t \t # "a = ": trailing spaces are ignored =b # " = b": empty key =c # " = c": empty key (override the above line) d= # "c = ": trailing comment is ignored sq = 'foo''b\\'ar' dq ="foo""''\\"" dq2 = foo""bar esc=a\\r\\n\\b\\tb foo\\bar = foo\\b\\\\ar foo\\bar::foo\\bar = baz [default1 default2]\t\t # space is allowed in section name fo =b ar # space allowed in value [emptysection] [doller ] foo=bar bar = $(foo) baz = 123$(default::bar)456${foo}798 qux = ${baz} quxx = $qux.$qux __EOC__ assert_equal(['default', 'default1 default2', 'doller', 'emptysection', 'foo', 'foo\\bar'], c.sections.sort) assert_equal(['', 'a', 'bar', 'baz', 'd', 'dq', 'dq2', 'esc', 'foo\\bar', 'sq'], c['default'].keys.sort) assert_equal('c', c['default']['']) assert_equal('', c['default']['a']) assert_equal('qx', c['default']['baz']) assert_equal('', c['default']['d']) assert_equal('baz', c['default']['bar']) assert_equal("foob'ar", c['default']['sq']) assert_equal("foo''\"", c['default']['dq']) assert_equal("foobar", c['default']['dq2']) assert_equal("a\r\n\b\tb", c['default']['esc']) assert_equal("foo\b\\ar", c['default']['foo\\bar']) assert_equal('baz', c['foo']['bar']) assert_equal('baz', c['foo\\bar']['foo\\bar']) assert_equal('b ar', c['default1 default2']['fo']) # dolloer assert_equal('bar', c['doller']['foo']) assert_equal('bar', c['doller']['bar']) assert_equal('123baz456bar798', c['doller']['baz']) assert_equal('123baz456bar798', c['doller']['qux']) assert_equal('123baz456bar798.123baz456bar798', c['doller']['quxx']) excn = assert_raise(OpenSSL::ConfigError) do OpenSSL::Config.parse("foo = $bar") end assert_equal("error in line 1: variable has no value", excn.message) excn = assert_raise(OpenSSL::ConfigError) do OpenSSL::Config.parse("foo = $(bar") end assert_equal("error in line 1: no close brace", excn.message) excn = assert_raise(OpenSSL::ConfigError) do OpenSSL::Config.parse("f o =b ar # no space in key") end assert_equal("error in line 1: missing equal sign", excn.message) excn = assert_raise(OpenSSL::ConfigError) do OpenSSL::Config.parse(<<__EOC__) # comment 1 # comments # # comment 2 \t#comment 3 [second ]\t [third # section not terminated __EOC__ end assert_equal("error in line 7: missing close square bracket", excn.message) end def test_s_load # alias of new c = OpenSSL::Config.load assert_equal("", c.to_s) assert_equal([], c.sections) # Tempfile.create("openssl.cnf") {|file| file.close c = OpenSSL::Config.load(file.path) assert_equal("[ default ]\n\n", c.to_s) assert_equal(['default'], c.sections) } end def test_initialize c = OpenSSL::Config.new assert_equal("", c.to_s) assert_equal([], c.sections) end def test_initialize_with_empty_file Tempfile.create("openssl.cnf") {|file| file.close c = OpenSSL::Config.new(file.path) assert_equal("[ default ]\n\n", c.to_s) assert_equal(['default'], c.sections) } end def test_initialize_with_example_file assert_equal(['CA_default', 'ca', 'default'], @it.sections.sort) end def test_get_value assert_equal('CA_default', @it.get_value('ca', 'default_ca')) assert_equal(nil, @it.get_value('ca', 'no such key')) assert_equal(nil, @it.get_value('no such section', 'no such key')) assert_equal('.', @it.get_value('', 'HOME')) assert_raise(TypeError) do @it.get_value(nil, 'HOME') # not allowed unlike Config#value end # fallback to 'default' ugly... assert_equal('.', @it.get_value('unknown', 'HOME')) end def test_get_value_ENV key = ENV.keys.first assert_not_nil(key) # make sure we have at least one ENV var. assert_equal(ENV[key], @it.get_value('ENV', key)) end def test_value # suppress deprecation warnings EnvUtil.suppress_warning do assert_equal('CA_default', @it.value('ca', 'default_ca')) assert_equal(nil, @it.value('ca', 'no such key')) assert_equal(nil, @it.value('no such section', 'no such key')) assert_equal('.', @it.value('', 'HOME')) assert_equal('.', @it.value(nil, 'HOME')) assert_equal('.', @it.value('HOME')) # fallback to 'default' ugly... assert_equal('.', @it.value('unknown', 'HOME')) end end def test_value_ENV EnvUtil.suppress_warning do key = ENV.keys.first assert_not_nil(key) # make sure we have at least one ENV var. assert_equal(ENV[key], @it.value('ENV', key)) end end def test_aref assert_equal({'HOME' => '.'}, @it['default']) assert_equal({'dir' => './demoCA', 'certs' => './certs'}, @it['CA_default']) assert_equal({}, @it['no_such_section']) assert_equal({}, @it['']) end def test_section EnvUtil.suppress_warning do assert_equal({'HOME' => '.'}, @it.section('default')) assert_equal({'dir' => './demoCA', 'certs' => './certs'}, @it.section('CA_default')) assert_equal({}, @it.section('no_such_section')) assert_equal({}, @it.section('')) end end def test_sections assert_equal(['CA_default', 'ca', 'default'], @it.sections.sort) @it['new_section'] = {'foo' => 'bar'} assert_equal(['CA_default', 'ca', 'default', 'new_section'], @it.sections.sort) @it['new_section'] = {} assert_equal(['CA_default', 'ca', 'default', 'new_section'], @it.sections.sort) end def test_add_value c = OpenSSL::Config.new assert_equal("", c.to_s) # add key c.add_value('default', 'foo', 'bar') assert_equal("[ default ]\nfoo=bar\n\n", c.to_s) # add another key c.add_value('default', 'baz', 'qux') assert_equal('bar', c['default']['foo']) assert_equal('qux', c['default']['baz']) # update the value c.add_value('default', 'baz', 'quxxx') assert_equal('bar', c['default']['foo']) assert_equal('quxxx', c['default']['baz']) # add section and key c.add_value('section', 'foo', 'bar') assert_equal('bar', c['default']['foo']) assert_equal('quxxx', c['default']['baz']) assert_equal('bar', c['section']['foo']) end def test_aset @it['foo'] = {'bar' => 'baz'} assert_equal({'bar' => 'baz'}, @it['foo']) @it['foo'] = {'bar' => 'qux', 'baz' => 'quxx'} assert_equal({'bar' => 'qux', 'baz' => 'quxx'}, @it['foo']) # OpenSSL::Config is add only for now. @it['foo'] = {'foo' => 'foo'} assert_equal({'foo' => 'foo', 'bar' => 'qux', 'baz' => 'quxx'}, @it['foo']) # you cannot override or remove any section and key. @it['foo'] = {} assert_equal({'foo' => 'foo', 'bar' => 'qux', 'baz' => 'quxx'}, @it['foo']) end def test_each # each returns [section, key, value] array. ary = @it.map { |e| e }.sort { |a, b| a[0] <=> b[0] } assert_equal(4, ary.size) assert_equal('CA_default', ary[0][0]) assert_equal('CA_default', ary[1][0]) assert_equal(["ca", "default_ca", "CA_default"], ary[2]) assert_equal(["default", "HOME", "."], ary[3]) end def test_to_s c = OpenSSL::Config.parse("[empty]\n") assert_equal("[ default ]\n\n[ empty ]\n\n", c.to_s) end def test_inspect assert_match(/#/, @it.inspect) end def test_freeze c = OpenSSL::Config.new c['foo'] = [['key', 'value']] c.freeze bug = '[ruby-core:18377]' # RuntimeError for 1.9, TypeError for 1.8 e = assert_raise(TypeError, bug) do c['foo'] = [['key', 'wrong']] end assert_match(/can't modify/, e.message, bug) end def test_dup assert(!@it.sections.empty?) c = @it.dup assert_equal(@it.sections.sort, c.sections.sort) @it['newsection'] = {'a' => 'b'} assert_not_equal(@it.sections.sort, c.sections.sort) end def test_clone assert(!@it.sections.empty?) c = @it.clone assert_equal(@it.sections.sort, c.sections.sort) @it['newsection'] = {'a' => 'b'} assert_not_equal(@it.sections.sort, c.sections.sort) end end end openssl-2.0.9/test/test_digest.rb000066400000000000000000000067371336157045000170500ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestDigest < OpenSSL::TestCase def setup super @d1 = OpenSSL::Digest.new("MD5") @d2 = OpenSSL::Digest::MD5.new end def test_digest null_hex = "d41d8cd98f00b204e9800998ecf8427e" null_bin = [null_hex].pack("H*") data = "DATA" hex = "e44f9e348e41cb272efa87387728571b" bin = [hex].pack("H*") assert_equal(null_bin, @d1.digest) assert_equal(null_hex, @d1.hexdigest) @d1 << data assert_equal(bin, @d1.digest) assert_equal(hex, @d1.hexdigest) assert_equal(bin, OpenSSL::Digest::MD5.digest(data)) assert_equal(hex, OpenSSL::Digest::MD5.hexdigest(data)) end def test_eql assert(@d1 == @d2, "==") d = @d1.clone assert(d == @d1, "clone") end def test_info assert_equal("MD5", @d1.name, "name") assert_equal("MD5", @d2.name, "name") assert_equal(16, @d1.size, "size") end def test_dup @d1.update("DATA") assert_equal(@d1.name, @d1.dup.name, "dup") assert_equal(@d1.name, @d1.clone.name, "clone") assert_equal(@d1.digest, @d1.clone.digest, "clone .digest") end def test_reset @d1.update("DATA") dig1 = @d1.digest @d1.reset @d1.update("DATA") dig2 = @d1.digest assert_equal(dig1, dig2, "reset") end def test_digest_constants algs = %w(MD4 MD5 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512) if !libressl? && !openssl?(1, 1, 0) algs += %w(DSS1 SHA) end algs.each do |alg| assert_not_nil(OpenSSL::Digest.new(alg)) klass = OpenSSL::Digest.const_get(alg) assert_not_nil(klass.new) end end def test_digest_by_oid_and_name check_digest(OpenSSL::ASN1::ObjectId.new("MD5")) check_digest(OpenSSL::ASN1::ObjectId.new("SHA1")) end def encode16(str) str.unpack("H*").first end def test_sha2 sha224_a = "abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5" sha256_a = "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb" sha384_a = "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31" sha512_a = "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75" assert_equal(sha224_a, OpenSSL::Digest::SHA224.hexdigest("a")) assert_equal(sha256_a, OpenSSL::Digest::SHA256.hexdigest("a")) assert_equal(sha384_a, OpenSSL::Digest::SHA384.hexdigest("a")) assert_equal(sha512_a, OpenSSL::Digest::SHA512.hexdigest("a")) assert_equal(sha224_a, encode16(OpenSSL::Digest::SHA224.digest("a"))) assert_equal(sha256_a, encode16(OpenSSL::Digest::SHA256.digest("a"))) assert_equal(sha384_a, encode16(OpenSSL::Digest::SHA384.digest("a"))) assert_equal(sha512_a, encode16(OpenSSL::Digest::SHA512.digest("a"))) end def test_digest_by_oid_and_name_sha2 check_digest(OpenSSL::ASN1::ObjectId.new("SHA224")) check_digest(OpenSSL::ASN1::ObjectId.new("SHA256")) check_digest(OpenSSL::ASN1::ObjectId.new("SHA384")) check_digest(OpenSSL::ASN1::ObjectId.new("SHA512")) end def test_openssl_digest assert_equal OpenSSL::Digest::MD5, OpenSSL::Digest("MD5") assert_raise NameError do OpenSSL::Digest("no such digest") end end private def check_digest(oid) d = OpenSSL::Digest.new(oid.sn) assert_not_nil(d) d = OpenSSL::Digest.new(oid.ln) assert_not_nil(d) d = OpenSSL::Digest.new(oid.oid) assert_not_nil(d) end end end openssl-2.0.9/test/test_engine.rb000066400000000000000000000050301336157045000170170ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) && defined?(OpenSSL::Engine) class OpenSSL::TestEngine < OpenSSL::TestCase def test_engines_free # [ruby-dev:44173] with_openssl <<-'end;' OpenSSL::Engine.load("openssl") OpenSSL::Engine.engines OpenSSL::Engine.engines end; end def test_openssl_engine_builtin with_openssl <<-'end;' orig = OpenSSL::Engine.engines pend "'openssl' is already loaded" if orig.any? { |e| e.id == "openssl" } engine = OpenSSL::Engine.load("openssl") assert_equal(true, engine) assert_equal(1, OpenSSL::Engine.engines.size - orig.size) end; end def test_openssl_engine_by_id_string with_openssl <<-'end;' orig = OpenSSL::Engine.engines pend "'openssl' is already loaded" if orig.any? { |e| e.id == "openssl" } engine = get_engine assert_not_nil(engine) assert_equal(1, OpenSSL::Engine.engines.size - orig.size) end; end def test_openssl_engine_id_name_inspect with_openssl <<-'end;' engine = get_engine assert_equal("openssl", engine.id) assert_not_nil(engine.name) assert_not_nil(engine.inspect) end; end def test_openssl_engine_digest_sha1 with_openssl <<-'end;' engine = get_engine digest = engine.digest("SHA1") assert_not_nil(digest) data = "test" assert_equal(OpenSSL::Digest::SHA1.digest(data), digest.digest(data)) end; end def test_openssl_engine_cipher_rc4 begin OpenSSL::Cipher.new("rc4") rescue OpenSSL::Cipher::CipherError pend "RC4 is not supported" end with_openssl(<<-'end;', ignore_stderr: true) engine = get_engine algo = "RC4" data = "a" * 1000 key = OpenSSL::Random.random_bytes(16) encrypted = crypt_data(data, key, :encrypt) { engine.cipher(algo) } decrypted = crypt_data(encrypted, key, :decrypt) { OpenSSL::Cipher.new(algo) } assert_equal(data, decrypted) end; end private # this is required because OpenSSL::Engine methods change global state def with_openssl(code, **opts) assert_separately([{ "OSSL_MDEBUG" => nil }, "-ropenssl"], <<~"end;", **opts) require #{__FILE__.dump} include OpenSSL::TestEngine::Utils #{code} end; end module Utils def get_engine OpenSSL::Engine.by_id("openssl") end def crypt_data(data, key, mode) cipher = yield cipher.send mode cipher.key = key cipher.update(data) + cipher.final end end end end openssl-2.0.9/test/test_fips.rb000066400000000000000000000003651336157045000165210ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestFIPS < OpenSSL::TestCase def test_fips_mode_is_reentrant OpenSSL.fips_mode = false OpenSSL.fips_mode = false end end end openssl-2.0.9/test/test_hmac.rb000066400000000000000000000030471336157045000164700ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestHMAC < OpenSSL::TestCase def test_hmac # RFC 2202 2. Test Cases for HMAC-MD5 hmac = OpenSSL::HMAC.new(["0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"].pack("H*"), "MD5") hmac.update("Hi There") assert_equal ["9294727a3638bb1c13f48ef8158bfc9d"].pack("H*"), hmac.digest assert_equal "9294727a3638bb1c13f48ef8158bfc9d", hmac.hexdigest # RFC 4231 4.2. Test Case 1 hmac = OpenSSL::HMAC.new(["0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"].pack("H*"), "SHA224") hmac.update("Hi There") assert_equal ["896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22"].pack("H*"), hmac.digest assert_equal "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22", hmac.hexdigest end def test_dup h1 = OpenSSL::HMAC.new("KEY", "MD5") h1.update("DATA") h = h1.dup assert_equal(h1.digest, h.digest, "dup digest") end def test_binary_update data = "Lücíllé: Bût... yøü sáîd hé wås âlrîght.\nDr. Físhmån: Yés. Hé's løst hîs léft hånd, sø hé's gøîng tø bé åll rîght" hmac = OpenSSL::HMAC.new("qShkcwN92rsM9nHfdnP4ugcVU2iI7iM/trovs01ZWok", "SHA256") result = hmac.update(data).hexdigest assert_equal "a13984b929a07912e4e21c5720876a8e150d6f67f854437206e7f86547248396", result end def test_reset_keep_key h1 = OpenSSL::HMAC.new("KEY", "MD5") first = h1.update("test").hexdigest h1.reset second = h1.update("test").hexdigest assert_equal first, second end end end openssl-2.0.9/test/test_ns_spki.rb000066400000000000000000000035251336157045000172270ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestNSSPI < OpenSSL::TestCase def setup super # This request data is adopt from the specification of # "Netscape Extensions for User Key Generation". # -- http://wp.netscape.com/eng/security/comm4-keygen.html @b64 = "MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue+PtwBRE6XfV" @b64 << "WtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID" @b64 << "AQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n/S" @b64 << "r/7iJNroWlSzSMtTiQTEB+ADWHGj9u1xrUrOilq/o2cuQxIfZcNZkYAkWP4DubqW" @b64 << "i0//rgBvmco=" end def test_build_data key1 = Fixtures.pkey("rsa1024") key2 = Fixtures.pkey("rsa2048") spki = OpenSSL::Netscape::SPKI.new spki.challenge = "RandomString" spki.public_key = key1.public_key spki.sign(key1, OpenSSL::Digest::SHA1.new) assert(spki.verify(spki.public_key)) assert(spki.verify(key1.public_key)) assert(!spki.verify(key2.public_key)) der = spki.to_der spki = OpenSSL::Netscape::SPKI.new(der) assert_equal("RandomString", spki.challenge) assert_equal(key1.public_key.to_der, spki.public_key.to_der) assert(spki.verify(spki.public_key)) assert_not_nil(spki.to_text) end def test_decode_data spki = OpenSSL::Netscape::SPKI.new(@b64) assert_equal(@b64, spki.to_pem) assert_equal(@b64.unpack("m").first, spki.to_der) assert_equal("MozillaIsMyFriend", spki.challenge) assert_equal(OpenSSL::PKey::RSA, spki.public_key.class) spki = OpenSSL::Netscape::SPKI.new(@b64.unpack("m").first) assert_equal(@b64, spki.to_pem) assert_equal(@b64.unpack("m").first, spki.to_der) assert_equal("MozillaIsMyFriend", spki.challenge) assert_equal(OpenSSL::PKey::RSA, spki.public_key.class) end end end openssl-2.0.9/test/test_ocsp.rb000066400000000000000000000304311336157045000165210ustar00rootroot00000000000000# frozen_string_literal: false require_relative "utils" if defined?(OpenSSL::TestUtils) class OpenSSL::TestOCSP < OpenSSL::TestCase def setup super # @ca_cert # | # @cert # |----------| # @cert2 @ocsp_cert ca_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA") @ca_key = Fixtures.pkey("rsa1024") ca_exts = [ ["basicConstraints", "CA:TRUE", true], ["keyUsage", "cRLSign,keyCertSign", true], ] @ca_cert = OpenSSL::TestUtils.issue_cert( ca_subj, @ca_key, 1, ca_exts, nil, nil) cert_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCA2") @cert_key = Fixtures.pkey("rsa1024") cert_exts = [ ["basicConstraints", "CA:TRUE", true], ["keyUsage", "cRLSign,keyCertSign", true], ] @cert = OpenSSL::TestUtils.issue_cert( cert_subj, @cert_key, 5, cert_exts, @ca_cert, @ca_key) cert2_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCert") @cert2_key = Fixtures.pkey("rsa1024") cert2_exts = [ ] @cert2 = OpenSSL::TestUtils.issue_cert( cert2_subj, @cert2_key, 10, cert2_exts, @cert, @cert_key) ocsp_subj = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=TestCAOCSP") @ocsp_key = Fixtures.pkey("rsa2048") ocsp_exts = [ ["extendedKeyUsage", "OCSPSigning", true], ] @ocsp_cert = OpenSSL::TestUtils.issue_cert( ocsp_subj, @ocsp_key, 100, ocsp_exts, @cert, @cert_key) end def test_new_certificate_id cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) assert_kind_of OpenSSL::OCSP::CertificateId, cid assert_equal @cert.serial, cid.serial cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA256.new) assert_kind_of OpenSSL::OCSP::CertificateId, cid assert_equal @cert.serial, cid.serial end def test_certificate_id_issuer_name_hash cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) assert_equal OpenSSL::Digest::SHA1.hexdigest(@cert.issuer.to_der), cid.issuer_name_hash assert_equal "d91f736ac4dc3242f0fb9b77a3149bd83c5c43d0", cid.issuer_name_hash end def test_certificate_id_issuer_key_hash cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) assert_equal OpenSSL::Digest::SHA1.hexdigest(OpenSSL::ASN1.decode(@ca_cert.to_der).value[0].value[6].value[1].value), cid.issuer_key_hash assert_equal "d1fef9fbf8ae1bc160cbfa03e2596dd873089213", cid.issuer_key_hash end def test_certificate_id_hash_algorithm cid_sha1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) cid_sha256 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA256.new) assert_equal "sha1", cid_sha1.hash_algorithm assert_equal "sha256", cid_sha256.hash_algorithm end def test_certificate_id_der cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) der = cid.to_der asn1 = OpenSSL::ASN1.decode(der) # hash algorithm defaults to SHA-1 assert_equal OpenSSL::ASN1.ObjectId("SHA1").to_der, asn1.value[0].value[0].to_der assert_equal [cid.issuer_name_hash].pack("H*"), asn1.value[1].value assert_equal [cid.issuer_key_hash].pack("H*"), asn1.value[2].value assert_equal @cert.serial, asn1.value[3].value assert_equal der, OpenSSL::OCSP::CertificateId.new(der).to_der end def test_certificate_id_dup cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) assert_equal cid.to_der, cid.dup.to_der end def test_request_der request = OpenSSL::OCSP::Request.new cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) request.add_certid(cid) request.sign(@cert, @cert_key, [@ca_cert], 0) asn1 = OpenSSL::ASN1.decode(request.to_der) assert_equal cid.to_der, asn1.value[0].value.find { |a| a.tag_class == :UNIVERSAL }.value[0].value[0].to_der assert_equal OpenSSL::ASN1.ObjectId("sha1WithRSAEncryption").to_der, asn1.value[1].value[0].value[0].value[0].to_der assert_equal @cert.to_der, asn1.value[1].value[0].value[2].value[0].value[0].to_der assert_equal @ca_cert.to_der, asn1.value[1].value[0].value[2].value[0].value[1].to_der assert_equal asn1.to_der, OpenSSL::OCSP::Request.new(asn1.to_der).to_der end def test_request_sign_verify cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) store = OpenSSL::X509::Store.new.add_cert(@ca_cert) # with signer cert req = OpenSSL::OCSP::Request.new.add_certid(cid) req.sign(@cert, @cert_key, []) assert_equal true, req.verify([], store) # without signer cert req = OpenSSL::OCSP::Request.new.add_certid(cid) req.sign(@cert, @cert_key, nil) assert_equal false, req.verify([@cert2], store) assert_equal false, req.verify([], store) # no signer assert_equal false, req.verify([], store, OpenSSL::OCSP::NOVERIFY) assert_equal true, req.verify([@cert], store, OpenSSL::OCSP::NOINTERN) ret = req.verify([@cert], store) if ret || openssl?(1, 0, 2) || libressl?(2, 4, 2) assert_equal true, ret else # RT2560; OCSP_request_verify() does not find signer cert from 'certs' when # OCSP_NOINTERN is not specified. # fixed by OpenSSL 1.0.1j, 1.0.2 and LibreSSL 2.4.2 pend "RT2560: ocsp_req_find_signer" end end def test_request_nonce req0 = OpenSSL::OCSP::Request.new req1 = OpenSSL::OCSP::Request.new.add_nonce("NONCE") req2 = OpenSSL::OCSP::Request.new.add_nonce("ABCDE") bres = OpenSSL::OCSP::BasicResponse.new assert_equal 2, req0.check_nonce(bres) bres.copy_nonce(req1) assert_equal 3, req0.check_nonce(bres) assert_equal 1, req1.check_nonce(bres) bres.add_nonce("NONCE") assert_equal 1, req1.check_nonce(bres) assert_equal 0, req2.check_nonce(bres) end def test_request_dup request = OpenSSL::OCSP::Request.new cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) request.add_certid(cid) assert_equal request.to_der, request.dup.to_der end def test_basic_response_der bres = OpenSSL::OCSP::BasicResponse.new cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, []) bres.add_nonce("NONCE") bres.sign(@ocsp_cert, @ocsp_key, [@ca_cert], 0) der = bres.to_der asn1 = OpenSSL::ASN1.decode(der) assert_equal OpenSSL::ASN1.Sequence([@ocsp_cert, @ca_cert]).to_der, asn1.value[3].value[0].to_der assert_equal der, OpenSSL::OCSP::BasicResponse.new(der).to_der rescue TypeError if /GENERALIZEDTIME/ =~ $!.message pend "OCSP_basic_sign() is broken" else raise end end def test_basic_response_sign_verify store = OpenSSL::X509::Store.new.add_cert(@ca_cert) # signed by CA bres = OpenSSL::OCSP::BasicResponse.new cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, "SHA256") bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, -400, -300, 500, []) bres.sign(@ca_cert, @ca_key, nil, 0, "SHA256") assert_equal false, bres.verify([], store) # signer not found assert_equal true, bres.verify([@ca_cert], store) bres.sign(@ca_cert, @ca_key, [], 0, "SHA256") assert_equal true, bres.verify([], store) # signed by OCSP signer bres = OpenSSL::OCSP::BasicResponse.new cid = OpenSSL::OCSP::CertificateId.new(@cert2, @cert) bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, -400, -300, 500, []) bres.sign(@ocsp_cert, @ocsp_key, [@cert]) assert_equal true, bres.verify([], store) assert_equal false, bres.verify([], store, OpenSSL::OCSP::NOCHAIN) # OpenSSL had a bug on this; test that our workaround works bres.sign(@ocsp_cert, @ocsp_key, []) assert_equal true, bres.verify([@cert], store) end def test_basic_response_dup bres = OpenSSL::OCSP::BasicResponse.new cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, []) bres.sign(@ocsp_cert, @ocsp_key, [@ca_cert], 0) assert_equal bres.to_der, bres.dup.to_der end def test_basic_response_response_operations bres = OpenSSL::OCSP::BasicResponse.new now = Time.at(Time.now.to_i) cid1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) cid2 = OpenSSL::OCSP::CertificateId.new(@ocsp_cert, @ca_cert, OpenSSL::Digest::SHA1.new) cid3 = OpenSSL::OCSP::CertificateId.new(@ca_cert, @ca_cert, OpenSSL::Digest::SHA1.new) bres.add_status(cid1, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, now - 400, -300, nil, nil) bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, nil, -300, 500, []) assert_equal 2, bres.responses.size single = bres.responses.first assert_equal cid1.to_der, single.certid.to_der assert_equal OpenSSL::OCSP::V_CERTSTATUS_REVOKED, single.cert_status assert_equal OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, single.revocation_reason assert_equal now - 400, single.revocation_time assert_in_delta (now - 301), single.this_update, 1 assert_equal nil, single.next_update assert_equal [], single.extensions assert_equal cid2.to_der, bres.find_response(cid2).certid.to_der assert_equal nil, bres.find_response(cid3) end def test_single_response_der bres = OpenSSL::OCSP::BasicResponse.new cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert) bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, nil, -300, 500, nil) single = bres.responses[0] der = single.to_der asn1 = OpenSSL::ASN1.decode(der) assert_equal :CONTEXT_SPECIFIC, asn1.value[1].tag_class assert_equal 0, asn1.value[1].tag # good assert_equal der, OpenSSL::OCSP::SingleResponse.new(der).to_der end def test_single_response_check_validity bres = OpenSSL::OCSP::BasicResponse.new cid1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) cid2 = OpenSSL::OCSP::CertificateId.new(@ocsp_cert, @ca_cert, OpenSSL::Digest::SHA1.new) bres.add_status(cid1, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, -400, -300, -50, []) bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, -400, -300, nil, []) bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, nil, Time.now + 100, nil, nil) if bres.responses[2].check_validity # thisUpdate is in future; must fail # LibreSSL bug; skip for now pend "OCSP_check_validity() is broken" end single1 = bres.responses[0] assert_equal false, single1.check_validity assert_equal false, single1.check_validity(30) assert_equal true, single1.check_validity(60) single2 = bres.responses[1] assert_equal true, single2.check_validity assert_equal true, single2.check_validity(0, 500) assert_equal false, single2.check_validity(0, 200) end def test_response bres = OpenSSL::OCSP::BasicResponse.new cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, []) bres.sign(@ocsp_cert, @ocsp_key, []) res = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, bres) assert_equal bres.to_der, res.basic.to_der assert_equal OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, res.status end def test_response_der bres = OpenSSL::OCSP::BasicResponse.new cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new) bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, []) bres.sign(@ocsp_cert, @ocsp_key, [@ca_cert], 0) res = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, bres) der = res.to_der asn1 = OpenSSL::ASN1.decode(der) assert_equal OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, asn1.value[0].value assert_equal OpenSSL::ASN1.ObjectId("basicOCSPResponse").to_der, asn1.value[1].value[0].value[0].to_der assert_equal bres.to_der, asn1.value[1].value[0].value[1].value assert_equal der, OpenSSL::OCSP::Response.new(der).to_der end def test_response_dup bres = OpenSSL::OCSP::BasicResponse.new bres.sign(@ocsp_cert, @ocsp_key, [@ca_cert], 0) res = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, bres) assert_equal res.to_der, res.dup.to_der end end end openssl-2.0.9/test/test_pair.rb000066400000000000000000000303351336157045000165130ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' require_relative 'ut_eof' if defined?(OpenSSL::TestUtils) module OpenSSL::SSLPairM def setup svr_dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost") ee_exts = [ ["keyUsage", "keyEncipherment,digitalSignature", true], ] @svr_key = OpenSSL::TestUtils::Fixtures.pkey("rsa1024") @svr_cert = issue_cert(svr_dn, @svr_key, 1, ee_exts, nil, nil) end def ssl_pair host = "127.0.0.1" tcps = create_tcp_server(host, 0) port = tcps.connect_address.ip_port th = Thread.new { sctx = OpenSSL::SSL::SSLContext.new sctx.cert = @svr_cert sctx.key = @svr_key sctx.tmp_dh_callback = proc { OpenSSL::TestUtils::Fixtures.pkey_dh("dh1024") } sctx.options |= OpenSSL::SSL::OP_NO_COMPRESSION ssls = OpenSSL::SSL::SSLServer.new(tcps, sctx) ns = ssls.accept ssls.close ns } tcpc = create_tcp_client(host, port) c = OpenSSL::SSL::SSLSocket.new(tcpc) c.connect s = th.value yield c, s ensure tcpc&.close tcps&.close s&.close end end module OpenSSL::SSLPair include OpenSSL::SSLPairM def create_tcp_server(host, port) TCPServer.new(host, port) end def create_tcp_client(host, port) TCPSocket.new(host, port) end end module OpenSSL::SSLPairLowlevelSocket include OpenSSL::SSLPairM def create_tcp_server(host, port) Addrinfo.tcp(host, port).listen end def create_tcp_client(host, port) Addrinfo.tcp(host, port).connect end end module OpenSSL::TestEOF1M def open_file(content) ssl_pair { |s1, s2| begin th = Thread.new { s2 << content; s2.close } yield s1 ensure th&.join end } end end module OpenSSL::TestEOF2M def open_file(content) ssl_pair { |s1, s2| begin th = Thread.new { s1 << content; s1.close } yield s2 ensure th&.join end } end end module OpenSSL::TestPairM def test_getc ssl_pair {|s1, s2| s1 << "a" assert_equal(?a, s2.getc) } end def test_gets ssl_pair {|s1, s2| s1 << "abc\n\n$def123ghi" s1.close ret = s2.gets assert_equal Encoding::BINARY, ret.encoding assert_equal "abc\n", ret assert_equal "\n$", s2.gets("$") assert_equal "def123", s2.gets(/\d+/) assert_equal "ghi", s2.gets assert_equal nil, s2.gets } end def test_gets_eof_limit ssl_pair {|s1, s2| s1.write("hello") s1.close # trigger EOF assert_match "hello", s2.gets("\n", 6), "[ruby-core:70149] [Bug #11400]" } end def test_readpartial ssl_pair {|s1, s2| s2.write "a\nbcd" assert_equal("a\n", s1.gets) result = "" result << s1.readpartial(10) until result.length == 3 assert_equal("bcd", result) s2.write "efg" result = "" result << s1.readpartial(10) until result.length == 3 assert_equal("efg", result) s2.close assert_raise(EOFError) { s1.readpartial(10) } assert_raise(EOFError) { s1.readpartial(10) } assert_equal("", s1.readpartial(0)) } end def test_readall ssl_pair {|s1, s2| s2.close assert_equal("", s1.read) } end def test_readline ssl_pair {|s1, s2| s2.close assert_raise(EOFError) { s1.readline } } end def test_puts_meta ssl_pair {|s1, s2| begin old = $/ $/ = '*' s1.puts 'a' ensure $/ = old end s1.close assert_equal("a\n", s2.read) } end def test_puts_empty ssl_pair {|s1, s2| s1.puts s1.close assert_equal("\n", s2.read) } end def test_multibyte_read_write # German a umlaut auml = [%w{ C3 A4 }.join('')].pack('H*') auml.force_encoding(Encoding::UTF_8) bsize = auml.bytesize ssl_pair { |s1, s2| assert_equal bsize, s1.write(auml) read = s2.read(bsize) assert_equal Encoding::ASCII_8BIT, read.encoding assert_equal bsize, read.bytesize assert_equal auml, read.force_encoding(Encoding::UTF_8) s1.puts(auml) read = s2.gets assert_equal Encoding::ASCII_8BIT, read.encoding assert_equal bsize + 1, read.bytesize assert_equal auml + "\n", read.force_encoding(Encoding::UTF_8) } end def test_read_nonblock ssl_pair {|s1, s2| err = nil assert_raise(OpenSSL::SSL::SSLErrorWaitReadable) { begin s2.read_nonblock(10) ensure err = $! end } assert_kind_of(IO::WaitReadable, err) s1.write "abc\ndef\n" IO.select([s2]) assert_equal("ab", s2.read_nonblock(2)) assert_equal("c\n", s2.gets) ret = nil assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10) } assert_equal("def\n", ret) s1.close IO.select([s2]) assert_raise(EOFError) { s2.read_nonblock(10) } } end def test_read_nonblock_no_exception ssl_pair {|s1, s2| assert_equal :wait_readable, s2.read_nonblock(10, exception: false) s1.write "abc\ndef\n" IO.select([s2]) assert_equal("ab", s2.read_nonblock(2, exception: false)) assert_equal("c\n", s2.gets) ret = nil assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10, exception: false) } assert_equal("def\n", ret) s1.close IO.select([s2]) assert_equal(nil, s2.read_nonblock(10, exception: false)) } end def test_read_with_outbuf ssl_pair { |s1, s2| s1.write("abc\n") buf = "" ret = s2.read(2, buf) assert_same ret, buf assert_equal "ab", ret buf = "garbage" ret = s2.read(2, buf) assert_same ret, buf assert_equal "c\n", ret buf = "garbage" assert_equal :wait_readable, s2.read_nonblock(100, buf, exception: false) assert_equal "", buf s1.close buf = "garbage" assert_equal nil, s2.read(100, buf) assert_equal "", buf } end def test_write_nonblock ssl_pair {|s1, s2| assert_equal 3, s1.write_nonblock("foo") assert_equal "foo", s2.read(3) data = "x" * 16384 written = 0 while true begin written += s1.write_nonblock(data) rescue IO::WaitWritable, IO::WaitReadable break end end assert written > 0 assert_equal written, s2.read(written).bytesize } end def test_write_nonblock_no_exceptions ssl_pair {|s1, s2| assert_equal 3, s1.write_nonblock("foo", exception: false) assert_equal "foo", s2.read(3) data = "x" * 16384 written = 0 while true case ret = s1.write_nonblock(data, exception: false) when :wait_readable, :wait_writable break else written += ret end end assert written > 0 assert_equal written, s2.read(written).bytesize } end def test_write_nonblock_with_buffered_data ssl_pair {|s1, s2| s1.write "foo" s1.write_nonblock("bar") s1.write "baz" s1.close assert_equal("foobarbaz", s2.read) } end def test_write_nonblock_with_buffered_data_no_exceptions ssl_pair {|s1, s2| s1.write "foo" s1.write_nonblock("bar", exception: false) s1.write "baz" s1.close assert_equal("foobarbaz", s2.read) } end def test_write_nonblock_retry ssl_pair {|s1, s2| # fill up a socket so we hit EAGAIN written = String.new n = 0 buf = 'a' * 4099 case ret = s1.write_nonblock(buf, exception: false) when :wait_readable then break when :wait_writable then break when Integer written << buf n += ret exp = buf.bytesize if ret != exp buf = buf.byteslice(ret, exp - ret) end end while true assert_kind_of Symbol, ret # make more space for subsequent write: readed = s2.read(n) assert_equal written, readed # this fails if SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER is missing: buf2 = Marshal.load(Marshal.dump(buf)) assert_kind_of Integer, s1.write_nonblock(buf2, exception: false) } end def test_write_zero ssl_pair {|s1, s2| assert_equal 0, s2.write_nonblock('', exception: false) assert_kind_of Symbol, s1.read_nonblock(1, exception: false) assert_equal 0, s2.syswrite('') assert_kind_of Symbol, s1.read_nonblock(1, exception: false) assert_equal 0, s2.write('') assert_kind_of Symbol, s1.read_nonblock(1, exception: false) } end def test_partial_tls_record_read_nonblock ssl_pair { |s1, s2| # the beginning of a TLS record s1.io.write("\x17") # should raise a IO::WaitReadable since a full TLS record is not available # for reading assert_raise(IO::WaitReadable) { s2.read_nonblock(1) } } end def tcp_pair host = "127.0.0.1" serv = TCPServer.new(host, 0) port = serv.connect_address.ip_port sock1 = TCPSocket.new(host, port) sock2 = serv.accept serv.close [sock1, sock2] ensure serv.close if serv && !serv.closed? end def test_connect_accept_nonblock_no_exception ctx2 = OpenSSL::SSL::SSLContext.new ctx2.cert = @svr_cert ctx2.key = @svr_key ctx2.tmp_dh_callback = proc { OpenSSL::TestUtils::Fixtures.pkey_dh("dh1024") } sock1, sock2 = tcp_pair s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) accepted = s2.accept_nonblock(exception: false) assert_equal :wait_readable, accepted ctx1 = OpenSSL::SSL::SSLContext.new s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1) th = Thread.new do rets = [] begin rv = s1.connect_nonblock(exception: false) rets << rv case rv when :wait_writable IO.select(nil, [s1], nil, 5) when :wait_readable IO.select([s1], nil, nil, 5) end end until rv == s1 rets end until th.join(0.01) accepted = s2.accept_nonblock(exception: false) assert_include([s2, :wait_readable, :wait_writable ], accepted) end rets = th.value assert_instance_of Array, rets rets.each do |rv| assert_include([s1, :wait_readable, :wait_writable ], rv) end ensure th.join if th s1.close if s1 s2.close if s2 sock1.close if sock1 sock2.close if sock2 accepted.close if accepted.respond_to?(:close) end def test_connect_accept_nonblock ctx = OpenSSL::SSL::SSLContext.new() ctx.cert = @svr_cert ctx.key = @svr_key ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::Fixtures.pkey_dh("dh1024") } sock1, sock2 = tcp_pair th = Thread.new { s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx) s2.sync_close = true begin sleep 0.2 s2.accept_nonblock rescue IO::WaitReadable IO.select([s2]) retry rescue IO::WaitWritable IO.select(nil, [s2]) retry end s2 } sleep 0.1 ctx = OpenSSL::SSL::SSLContext.new() s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx) begin sleep 0.2 s1.connect_nonblock rescue IO::WaitReadable IO.select([s1]) retry rescue IO::WaitWritable IO.select(nil, [s1]) retry end s1.sync_close = true s2 = th.value s1.print "a\ndef" assert_equal("a\n", s2.gets) ensure th.join if th s1.close if s1 && !s1.closed? s2.close if s2 && !s2.closed? sock1.close if sock1 && !sock1.closed? sock2.close if sock2 && !sock2.closed? end end class OpenSSL::TestEOF1 < OpenSSL::TestCase include OpenSSL::TestEOF include OpenSSL::SSLPair include OpenSSL::TestEOF1M end class OpenSSL::TestEOF1LowlevelSocket < OpenSSL::TestCase include OpenSSL::TestEOF include OpenSSL::SSLPairLowlevelSocket include OpenSSL::TestEOF1M end class OpenSSL::TestEOF2 < OpenSSL::TestCase include OpenSSL::TestEOF include OpenSSL::SSLPair include OpenSSL::TestEOF2M end class OpenSSL::TestEOF2LowlevelSocket < OpenSSL::TestCase include OpenSSL::TestEOF include OpenSSL::SSLPairLowlevelSocket include OpenSSL::TestEOF2M end class OpenSSL::TestPair < OpenSSL::TestCase include OpenSSL::SSLPair include OpenSSL::TestPairM end class OpenSSL::TestPairLowlevelSocket < OpenSSL::TestCase include OpenSSL::SSLPairLowlevelSocket include OpenSSL::TestPairM end end openssl-2.0.9/test/test_pkcs12.rb000066400000000000000000000251441336157045000166650ustar00rootroot00000000000000# frozen_string_literal: false require_relative "utils" if defined?(OpenSSL::TestUtils) module OpenSSL class TestPKCS12 < OpenSSL::TestCase def setup super ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA") ca_exts = [ ["basicConstraints","CA:TRUE",true], ["keyUsage","keyCertSign, cRLSign",true], ["subjectKeyIdentifier","hash",false], ["authorityKeyIdentifier","keyid:always",false], ] @cacert = issue_cert(ca, Fixtures.pkey("rsa2048"), 1, ca_exts, nil, nil) inter_ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Intermediate CA") inter_ca_key = OpenSSL::PKey.read <<-_EOS_ -----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQDp7hIG0SFMG/VWv1dBUWziAPrNmkMXJgTCAoB7jffzRtyyN04K oq/89HAszTMStZoMigQURfokzKsjpUp8OYCAEsBtt9d5zPndWMz/gHN73GrXk3LT ZsxEn7Xv5Da+Y9F/Hx2QZUHarV5cdZixq2NbzWGwrToogOQMh2pxN3Z/0wIDAQAB AoGBAJysUyx3olpsGzv3OMRJeahASbmsSKTXVLZvoIefxOINosBFpCIhZccAG6UV 5c/xCvS89xBw8aD15uUfziw3AuT8QPEtHCgfSjeT7aWzBfYswEgOW4XPuWr7EeI9 iNHGD6z+hCN/IQr7FiEBgTp6A+i/hffcSdR83fHWKyb4M7TRAkEA+y4BNd668HmC G5MPRx25n6LixuBxrNp1umfjEI6UZgEFVpYOg4agNuimN6NqM253kcTR94QNTUs5 Kj3EhG1YWwJBAO5rUjiOyCNVX2WUQrOMYK/c1lU7fvrkdygXkvIGkhsPoNRzLPeA HGJszKtrKD8bNihWpWNIyqKRHfKVD7yXT+kCQGCAhVCIGTRoypcDghwljHqLnysf ci0h5ZdPcIqc7ODfxYhFsJ/Rql5ONgYsT5Ig/+lOQAkjf+TRYM4c2xKx2/8CQBvG jv6dy70qDgIUgqzONtlmHeYyFzn9cdBO5sShdVYHvRHjFSMEXsosqK9zvW2UqvuK FJx7d3f29gkzynCLJDkCQGQZlEZJC4vWmWJGRKJ24P6MyQn3VsPfErSKOg4lvyM3 Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es= -----END RSA PRIVATE KEY----- _EOS_ @inter_cacert = issue_cert(inter_ca, inter_ca_key, 2, ca_exts, @cacert, Fixtures.pkey("rsa2048")) exts = [ ["keyUsage","digitalSignature",true], ["subjectKeyIdentifier","hash",false], ] ee = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Ruby PKCS12 Test Certificate") @mykey = Fixtures.pkey("rsa1024") @mycert = issue_cert(ee, @mykey, 3, exts, @inter_cacert, inter_ca_key) end def test_create pkcs12 = OpenSSL::PKCS12.create( "omg", "hello", @mykey, @mycert ) assert_equal @mycert.to_der, pkcs12.certificate.to_der assert_equal @mykey.to_der, pkcs12.key.to_der assert_nil pkcs12.ca_certs end def test_create_no_pass pkcs12 = OpenSSL::PKCS12.create( nil, "hello", @mykey, @mycert ) assert_equal @mycert.to_der, pkcs12.certificate.to_der assert_equal @mykey.to_der, pkcs12.key.to_der assert_nil pkcs12.ca_certs decoded = OpenSSL::PKCS12.new(pkcs12.to_der) assert_cert @mycert, decoded.certificate end def test_create_with_chain chain = [@inter_cacert, @cacert] pkcs12 = OpenSSL::PKCS12.create( "omg", "hello", @mykey, @mycert, chain ) assert_equal chain, pkcs12.ca_certs end def test_create_with_chain_decode chain = [@cacert, @inter_cacert] passwd = "omg" pkcs12 = OpenSSL::PKCS12.create( passwd, "hello", @mykey, @mycert, chain ) decoded = OpenSSL::PKCS12.new(pkcs12.to_der, passwd) assert_equal chain.size, decoded.ca_certs.size assert_include_cert @cacert, decoded.ca_certs assert_include_cert @inter_cacert, decoded.ca_certs assert_cert @mycert, decoded.certificate assert_equal @mykey.to_der, decoded.key.to_der end def test_create_with_bad_nid assert_raise(ArgumentError) do OpenSSL::PKCS12.create( "omg", "hello", @mykey, @mycert, [], "foo" ) end end def test_create_with_itr OpenSSL::PKCS12.create( "omg", "hello", @mykey, @mycert, [], nil, nil, 2048 ) assert_raise(TypeError) do OpenSSL::PKCS12.create( "omg", "hello", @mykey, @mycert, [], nil, nil, "omg" ) end end def test_create_with_mac_itr OpenSSL::PKCS12.create( "omg", "hello", @mykey, @mycert, [], nil, nil, nil, 2048 ) assert_raise(TypeError) do OpenSSL::PKCS12.create( "omg", "hello", @mykey, @mycert, [], nil, nil, nil, "omg" ) end end def test_new_with_one_key_and_one_cert # generated with: # openssl version #=> OpenSSL 1.0.2h 3 May 2016 # openssl pkcs12 -in <@mycert> -inkey -export -out str = <<~EOF.unpack("m").first MIIGQQIBAzCCBgcGCSqGSIb3DQEHAaCCBfgEggX0MIIF8DCCAu8GCSqGSIb3DQEH BqCCAuAwggLcAgEAMIIC1QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIeZPM Rh6KiXgCAggAgIICqL6O+LCZmBzdIg6mozPF3FpY0hVbWHvTNMiDHieW3CrAanhN YCH2/wHqH8WpFpEWwF0qEEXAWjHsIlYB4Cfqo6b7XpuZe5eVESsjNTOTMF1JCUJj A6iNefXmCFLync1JK5LUodRDhTlKLU1WPK20X9X4vuEwHn8wt5RUb8P0E+Xh6rpS XC4LkZKT45zF3cJa/n5+dW65ohVGNVnF9D1bCNEKHMOllK1V9omutQ9slW88hpga LGiFsJoFOb/ESGb78KO+bd6zbX1MdKdBV+WD6t1uF/cgU65y+2A4nXs1urda+MJ7 7iVqiB7Vnc9cANTbAkTSGNyoUDVM/NZde782/8IvddLAzUZ2EftoRDke6PvuBOVL ljBhNWmdamrtBqzuzVZCRdWq44KZkF2Xoc9asepwIkdVmntzQF7f1Z+Ta5yg6HFp xnr7CuM+MlHEShXkMgYtHnwAq10fDMSXIvjhi/AA5XUAusDO3D+hbtcRDcJ4uUes dm5dhQE2qJ02Ysn4aH3o1F3RYNOzrxejHJwl0D2TCE8Ww2X342xib57+z9u03ufj jswhiMKxy67f1LhUMq3XrT3uV6kCVXk/KUOUPcXPlPVNA5JmZeFhMp6GrtB5xJJ9 wwBZD8UL5A2U2Mxi2OZsdUBv8eo3jnjZ284aFpt+mCjIHrLW5O0jwY8OCwSlYUoY IY00wlabX0s82kBcIQNZbC1RSV2267ro/7A0MClc8YQ/zWN0FKY6apgtUkHJI1cL 1dc77mhnjETjwW94iLMDFy4zQfVu7IfCBqOBzygRNnqqUG66UhTs1xFnWM0mWXl/ Zh9+AMpbRLIPaKCktIjl5juzzm+KEgkhD+707XRCFIGUYGP5bSHzGaz8PK9hj0u1 E2SpZHUvYOcawmxtA7pmpSxl5uQjMIIC+QYJKoZIhvcNAQcBoIIC6gSCAuYwggLi MIIC3gYLKoZIhvcNAQwKAQKgggKmMIICojAcBgoqhkiG9w0BDAEDMA4ECKB338m8 qSzHAgIIAASCAoACFhJeqA3xx+s1qIH6udNQYY5hAL6oz7SXoGwFhDiceSyJjmAD Dby9XWM0bPl1Gj5nqdsuI/lAM++fJeoETk+rxw8q6Ofk2zUaRRE39qgpwBwSk44o 0SAFJ6bzHpc5CFh6sZmDaUX5Lm9GtjnGFmmsPTSJT5an5JuJ9WczGBEd0nSBQhJq xHbTGZiN8i3SXcIH531Sub+CBIFWy5lyCKgDYh/kgJFGQAaWUOjLI+7dCEESonXn F3Jh2uPbnDF9MGJyAFoNgWFhgSpi1cf6AUi87GY4Oyur88ddJ1o0D0Kz2uw8/bpG s3O4PYnIW5naZ8mozzbnYByEFk7PoTwM7VhoFBfYNtBoAI8+hBnPY/Y71YUojEXf SeX6QbtkIANfzS1XuFNKElShC3DPQIHpKzaatEsfxHfP+8VOav6zcn4mioao7NHA x7Dp6R1enFGoQOq4UNjBT8YjnkG5vW8zQHW2dAHLTJBq6x2Fzm/4Pjo/8vM1FiGl BQdW5vfDeJ/l6NgQm3xR9ka2E2HaDqIcj1zWbN8jy/bHPFJYuF/HH8MBV/ngMIXE vFEW/ToYv8eif0+EpUtzBsCKD4a7qYYYh87RmEVoQU96q6m+UbhpD2WztYfAPkfo OSL9j2QHhVczhL7OAgqNeM95pOsjA9YMe7exTeqK31LYnTX8oH8WJD1xGbRSJYgu SY6PQbumcJkc/TFPn0GeVUpiDdf83SeG50lo/i7UKQi2l1hi5Y51fQhnBnyMr68D llSZEvSWqfDxBJkBpeg6PIYvkTpEwKRJpVQoM3uYvdqVSSnW6rydqIb+snfOrlhd f+xCtq9xr+kHeTSqLIDRRAnMfgFRhY3IBlj6MSUwIwYJKoZIhvcNAQkVMRYEFBdb 8XGWehZ6oPj56Pf/uId46M9AMDEwITAJBgUrDgMCGgUABBRvSCB04/f8f13pp2PF vyl2WuMdEwQIMWFFphPkIUICAggA EOF p12 = OpenSSL::PKCS12.new(str, "abc123") assert_equal @mykey.to_der, p12.key.to_der assert_equal @mycert.subject.to_der, p12.certificate.subject.to_der assert_equal [], Array(p12.ca_certs) end def test_new_with_no_keys # generated with: # openssl pkcs12 -in <@mycert> -nokeys -export -out str = <<~EOF.unpack("m").first MIIDHAIBAzCCAuIGCSqGSIb3DQEHAaCCAtMEggLPMIICyzCCAscGCSqGSIb3DQEH BqCCArgwggK0AgEAMIICrQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIX4+W irqwH40CAggAgIICgOaCyo+5+6IOVoGCCL80c50bkkzAwqdXxvkKExJSdcJz2uMU 0gRrKnZEjL5wrUsN8RwZu8DvgQTEhNEkKsUgM7AWainmN/EnwohIdHZAHpm6WD67 I9kLGp0/DHrqZrV9P2dLfhXLUSQE8PI0tqZPZ8UEABhizkViw4eISTkrOUN7pGbN Qtx/oqgitXDuX2polbxYYDwt9vfHZhykHoKgew26SeJyZfeMs/WZ6olEI4cQUAFr mvYGuC1AxEGTo9ERmU8Pm16j9Hr9PFk50WYe+rnk9oX3wJogQ7XUWS5kYf7XRycd NDkNiwV/ts94bbuaGZp1YA6I48FXpIc8b5fX7t9tY0umGaWy0bARe1L7o0Y89EPe lMg25rOM7j3uPtFG8whbSfdETSy57UxzzTcJ6UwexeaK6wb2jqEmj5AOoPLWeaX0 LyOAszR3v7OPAcjIDYZGdrbb3MZ2f2vo2pdQfu9698BrWhXuM7Odh73RLhJVreNI aezNOAtPyBlvGiBQBGTzRIYHSLL5Y5aVj2vWLAa7hjm5qTL5C5mFdDIo6TkEMr6I OsexNQofEGs19kr8nARXDlcbEimk2VsPj4efQC2CEXZNzURsKca82pa62MJ8WosB DTFd8X06zZZ4nED50vLopZvyW4fyW60lELwOyThAdG8UchoAaz2baqP0K4de44yM Y5/yPFDu4+GoimipJfbiYviRwbzkBxYW8+958ILh0RtagLbvIGxbpaym9PqGjOzx ShNXjLK2aAFZsEizQ8kd09quJHU/ogq2cUXdqqhmOqPnUWrJVi/VCoRB3Pv1/lE4 mrUgr2YZ11rYvBw6g5XvNvFcSc53OKyV7SLn0dwwMTAhMAkGBSsOAwIaBQAEFEWP 1WRQykaoD4uJCpTx/wv0SLLBBAiDKI26LJK7xgICCAA= EOF p12 = OpenSSL::PKCS12.new(str, "abc123") assert_equal nil, p12.key assert_equal nil, p12.certificate assert_equal 1, p12.ca_certs.size assert_equal @mycert.subject.to_der, p12.ca_certs[0].subject.to_der end def test_new_with_no_certs # generated with: # openssl pkcs12 -inkey -nocerts -export -out str = <<~EOF.unpack("m").first MIIDJwIBAzCCAu0GCSqGSIb3DQEHAaCCAt4EggLaMIIC1jCCAtIGCSqGSIb3DQEH AaCCAsMEggK/MIICuzCCArcGCyqGSIb3DQEMCgECoIICpjCCAqIwHAYKKoZIhvcN AQwBAzAOBAg6AaYnJs84SwICCAAEggKAQzZH+fWSpcQYD1J7PsGSune85A++fLCQ V7tacp2iv95GJkxwYmfTP176pJdgs00mceB9UJ/u9EX5nD0djdjjQjwo6sgKjY0q cpVhZw8CMxw7kBD2dhtui0zT8z5hy03LePxsjEKsGiSbeVeeGbSfw/I6AAYbv+Uh O/YPBGumeHj/D2WKnfsHJLQ9GAV3H6dv5VKYNxjciK7f/JEyZCuUQGIN64QFHDhJ 7fzLqd/ul3FZzJZO6a+dwvcgux09SKVXDRSeFmRCEX4b486iWhJJVspCo9P2KNne ORrpybr3ZSwxyoICmjyo8gj0OSnEfdx9790Ej1takPqSA1wIdSdBLekbZqB0RBQg DEuPOsXNo3QFi8ji1vu0WBRJZZSNC2hr5NL6lNR+DKxG8yzDll2j4W4BBIp22mAE 7QRX7kVxu17QJXQhOUac4Dd1qXmzebP8t6xkAxD9L7BWEN5OdiXWwSWGjVjMBneX nYObi/3UT/aVc5WHMHK2BhCI1bwH51E6yZh06d5m0TQpYGUTWDJdWGBSrp3A+8jN N2PMQkWBFrXP3smHoTEN4oZC4FWiPsIEyAkQsfKRhcV9lGKl2Xgq54ROTFLnwKoj Z3zJScnq9qmNzvVZSMmDLkjLyDq0pxRxGKBvgouKkWY7VFFIwwBIJM39iDJ5NbBY i1AQFTRsRSsZrNVPasCXrIq7bhMoJZb/YZOGBLNyJVqKUoYXhtwsajzSq54VlWft JxsPayEd4Vi6O9EU1ahnj6qFEZiKFzsicgK2J1Rb8cYagrp0XWjHW0SBn5GVUWCg GUokSFG/0JTdeYTo/sQuG4qNgJkOolRjpeI48Fciq5VUWLvVdKioXzAxMCEwCQYF Kw4DAhoFAAQUYAuwVtGD1TdgbFK4Yal2XBgwUR4ECEawsN3rNaa6AgIIAA== EOF p12 = OpenSSL::PKCS12.new(str, "abc123") assert_equal @mykey.to_der, p12.key.to_der assert_equal nil, p12.certificate assert_equal [], Array(p12.ca_certs) end def test_dup p12 = OpenSSL::PKCS12.create("pass", "name", @mykey, @mycert) assert_equal p12.to_der, p12.dup.to_der end private def assert_cert expected, actual [ :subject, :issuer, :serial, :not_before, :not_after, ].each do |attribute| assert_equal expected.send(attribute), actual.send(attribute) end assert_equal expected.to_der, actual.to_der end def assert_include_cert cert, ary der = cert.to_der ary.each do |candidate| if candidate.to_der == der return true end end false end end end end openssl-2.0.9/test/test_pkcs5.rb000066400000000000000000000054241336157045000166060ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestPKCS5 < OpenSSL::TestCase def test_pbkdf2_hmac_sha1_rfc6070_c_1_len_20 p ="password" s = "salt" c = 1 dk_len = 20 raw = %w{ 0c 60 c8 0f 96 1f 0e 71 f3 a9 b5 24 af 60 12 06 2f e0 37 a6 } expected = [raw.join('')].pack('H*') value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) assert_equal(expected, value) end def test_pbkdf2_hmac_sha1_rfc6070_c_2_len_20 p ="password" s = "salt" c = 2 dk_len = 20 raw = %w{ ea 6c 01 4d c7 2d 6f 8c cd 1e d9 2a ce 1d 41 f0 d8 de 89 57 } expected = [raw.join('')].pack('H*') value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) assert_equal(expected, value) end def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_20 p ="password" s = "salt" c = 4096 dk_len = 20 raw = %w{ 4b 00 79 01 b7 65 48 9a be ad 49 d9 26 f7 21 d0 65 a4 29 c1 } expected = [raw.join('')].pack('H*') value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) assert_equal(expected, value) end # takes too long! # def test_pbkdf2_hmac_sha1_rfc6070_c_16777216_len_20 # p ="password" # s = "salt" # c = 16777216 # dk_len = 20 # raw = %w{ ee fe 3d 61 cd 4d a4 e4 # e9 94 5b 3d 6b a2 15 8c # 26 34 e9 84 } # expected = [raw.join('')].pack('H*') # value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) # assert_equal(expected, value) # end def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_25 p ="passwordPASSWORDpassword" s = "saltSALTsaltSALTsaltSALTsaltSALTsalt" c = 4096 dk_len = 25 raw = %w{ 3d 2e ec 4f e4 1c 84 9b 80 c8 d8 36 62 c0 e4 4a 8b 29 1a 96 4c f2 f0 70 38 } expected = [raw.join('')].pack('H*') value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) assert_equal(expected, value) end def test_pbkdf2_hmac_sha1_rfc6070_c_4096_len_16 p ="pass\0word" s = "sa\0lt" c = 4096 dk_len = 16 raw = %w{ 56 fa 6a a7 55 48 09 9d cc 37 d7 f0 34 25 e0 c3 } expected = [raw.join('')].pack('H*') value = OpenSSL::PKCS5.pbkdf2_hmac_sha1(p, s, c, dk_len) assert_equal(expected, value) end def test_pbkdf2_hmac_sha256_c_20000_len_32 #unfortunately no official test vectors available yet for SHA-2 p ="password" s = OpenSSL::Random.random_bytes(16) c = 20000 dk_len = 32 digest = OpenSSL::Digest::SHA256.new value1 = OpenSSL::PKCS5.pbkdf2_hmac(p, s, c, dk_len, digest) value2 = OpenSSL::PKCS5.pbkdf2_hmac(p, s, c, dk_len, digest) assert_equal(value1, value2) end if OpenSSL::PKCS5.respond_to?(:pbkdf2_hmac) end end openssl-2.0.9/test/test_pkcs7.rb000066400000000000000000000266711336157045000166170ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestPKCS7 < OpenSSL::TestCase def setup super @rsa1024 = Fixtures.pkey("rsa1024") @rsa2048 = Fixtures.pkey("rsa2048") ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA") ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1") ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2") ca_exts = [ ["basicConstraints","CA:TRUE",true], ["keyUsage","keyCertSign, cRLSign",true], ["subjectKeyIdentifier","hash",false], ["authorityKeyIdentifier","keyid:always",false], ] @ca_cert = issue_cert(ca, @rsa2048, 1, ca_exts, nil, nil) ee_exts = [ ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true], ["authorityKeyIdentifier","keyid:always",false], ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false], ] @ee1_cert = issue_cert(ee1, @rsa1024, 2, ee_exts, @ca_cert, @rsa2048) @ee2_cert = issue_cert(ee2, @rsa1024, 3, ee_exts, @ca_cert, @rsa2048) end def test_signed store = OpenSSL::X509::Store.new store.add_cert(@ca_cert) ca_certs = [@ca_cert] data = "aaaaa\r\nbbbbb\r\nccccc\r\n" tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs) p7 = OpenSSL::PKCS7.new(tmp.to_der) certs = p7.certificates signers = p7.signers assert(p7.verify([], store)) assert_equal(data, p7.data) assert_equal(2, certs.size) assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s) assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s) assert_equal(1, signers.size) assert_equal(@ee1_cert.serial, signers[0].serial) assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s) # Normally OpenSSL tries to translate the supplied content into canonical # MIME format (e.g. a newline character is converted into CR+LF). # If the content is a binary, PKCS7::BINARY flag should be used. data = "aaaaa\nbbbbb\nccccc\n" flag = OpenSSL::PKCS7::BINARY tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs, flag) p7 = OpenSSL::PKCS7.new(tmp.to_der) certs = p7.certificates signers = p7.signers assert(p7.verify([], store)) assert_equal(data, p7.data) assert_equal(2, certs.size) assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s) assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s) assert_equal(1, signers.size) assert_equal(@ee1_cert.serial, signers[0].serial) assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s) # A signed-data which have multiple signatures can be created # through the following steps. # 1. create two signed-data # 2. copy signerInfo and certificate from one to another tmp1 = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, [], flag) tmp2 = OpenSSL::PKCS7.sign(@ee2_cert, @rsa1024, data, [], flag) tmp1.add_signer(tmp2.signers[0]) tmp1.add_certificate(@ee2_cert) p7 = OpenSSL::PKCS7.new(tmp1.to_der) certs = p7.certificates signers = p7.signers assert(p7.verify([], store)) assert_equal(data, p7.data) assert_equal(2, certs.size) assert_equal(2, signers.size) assert_equal(@ee1_cert.serial, signers[0].serial) assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s) assert_equal(@ee2_cert.serial, signers[1].serial) assert_equal(@ee2_cert.issuer.to_s, signers[1].issuer.to_s) end def test_detached_sign store = OpenSSL::X509::Store.new store.add_cert(@ca_cert) ca_certs = [@ca_cert] data = "aaaaa\nbbbbb\nccccc\n" flag = OpenSSL::PKCS7::BINARY|OpenSSL::PKCS7::DETACHED tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs, flag) p7 = OpenSSL::PKCS7.new(tmp.to_der) assert_nothing_raised do OpenSSL::ASN1.decode(p7) end certs = p7.certificates signers = p7.signers assert(!p7.verify([], store)) assert(p7.verify([], store, data)) assert_equal(data, p7.data) assert_equal(2, certs.size) assert_equal(@ee1_cert.subject.to_s, certs[0].subject.to_s) assert_equal(@ca_cert.subject.to_s, certs[1].subject.to_s) assert_equal(1, signers.size) assert_equal(@ee1_cert.serial, signers[0].serial) assert_equal(@ee1_cert.issuer.to_s, signers[0].issuer.to_s) end def test_enveloped certs = [@ee1_cert, @ee2_cert] cipher = OpenSSL::Cipher::AES.new("128-CBC") data = "aaaaa\nbbbbb\nccccc\n" tmp = OpenSSL::PKCS7.encrypt(certs, data, cipher, OpenSSL::PKCS7::BINARY) p7 = OpenSSL::PKCS7.new(tmp.to_der) recip = p7.recipients assert_equal(:enveloped, p7.type) assert_equal(2, recip.size) assert_equal(@ca_cert.subject.to_s, recip[0].issuer.to_s) assert_equal(2, recip[0].serial) assert_equal(data, p7.decrypt(@rsa1024, @ee1_cert)) assert_equal(@ca_cert.subject.to_s, recip[1].issuer.to_s) assert_equal(3, recip[1].serial) assert_equal(data, p7.decrypt(@rsa1024, @ee2_cert)) end def test_graceful_parsing_failure #[ruby-core:43250] contents = File.read(__FILE__) assert_raise(ArgumentError) { OpenSSL::PKCS7.new(contents) } end def test_set_type_signed p7 = OpenSSL::PKCS7.new p7.type = "signed" assert_equal(:signed, p7.type) end def test_set_type_data p7 = OpenSSL::PKCS7.new p7.type = "data" assert_equal(:data, p7.type) end def test_set_type_signed_and_enveloped p7 = OpenSSL::PKCS7.new p7.type = "signedAndEnveloped" assert_equal(:signedAndEnveloped, p7.type) end def test_set_type_enveloped p7 = OpenSSL::PKCS7.new p7.type = "enveloped" assert_equal(:enveloped, p7.type) end def test_set_type_encrypted p7 = OpenSSL::PKCS7.new p7.type = "encrypted" assert_equal(:encrypted, p7.type) end def test_degenerate_pkcs7 ca_cert_pem = < OpenSSL::PKey::DH::DEFAULT_1024, 2048 => OpenSSL::PKey::DH::DEFAULT_2048, } list.each do |expected_size, dh| assert_equal expected_size, dh.p.num_bits assert_predicate dh.p, :prime? result, remainder = (dh.p - 1) / 2 assert_predicate result, :prime? assert_equal 0, remainder assert_no_key dh end end def test_new dh = OpenSSL::PKey::DH.new(NEW_KEYLEN) assert_key(dh) end def test_new_break assert_nil(OpenSSL::PKey::DH.new(NEW_KEYLEN) { break }) assert_raise(RuntimeError) do OpenSSL::PKey::DH.new(NEW_KEYLEN) { raise } end end def test_DHparams dh1024 = Fixtures.pkey_dh("dh1024") asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(dh1024.p), OpenSSL::ASN1::Integer(dh1024.g) ]) key = OpenSSL::PKey::DH.new(asn1.to_der) assert_same_dh dup_public(dh1024), key pem = <<~EOF -----BEGIN DH PARAMETERS----- MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0 pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC -----END DH PARAMETERS----- EOF key = OpenSSL::PKey::DH.new(pem) assert_same_dh dup_public(dh1024), key assert_equal asn1.to_der, dh1024.to_der assert_equal pem, dh1024.export end def test_public_key dh = Fixtures.pkey_dh("dh1024") public_key = dh.public_key assert_no_key(public_key) #implies public_key.public? is false! assert_equal(dh.to_der, public_key.to_der) assert_equal(dh.to_pem, public_key.to_pem) end def test_generate_key dh = Fixtures.pkey_dh("dh1024").public_key # creates a copy assert_no_key(dh) dh.generate_key! assert_key(dh) end def test_key_exchange dh = Fixtures.pkey_dh("dh1024") dh2 = dh.public_key dh.generate_key! dh2.generate_key! assert_equal(dh.compute_key(dh2.pub_key), dh2.compute_key(dh.pub_key)) end def test_dup dh = OpenSSL::PKey::DH.new(NEW_KEYLEN) dh2 = dh.dup assert_equal dh.to_der, dh2.to_der # params assert_equal_params dh, dh2 # keys dh2.set_pqg(dh2.p + 1, nil, dh2.g) assert_not_equal dh2.p, dh.p assert_equal dh2.g, dh.g end private def assert_equal_params(dh1, dh2) assert_equal(dh1.g, dh2.g) assert_equal(dh1.p, dh2.p) end def assert_no_key(dh) assert_equal(false, dh.public?) assert_equal(false, dh.private?) assert_equal(nil, dh.pub_key) assert_equal(nil, dh.priv_key) end def assert_key(dh) assert(dh.public?) assert(dh.private?) assert(dh.pub_key) assert(dh.priv_key) end def assert_same_dh(expected, key) check_component(expected, key, [:p, :q, :g, :pub_key, :priv_key]) end end end openssl-2.0.9/test/test_pkey_dsa.rb000066400000000000000000000152711336157045000173610ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase def test_private key = OpenSSL::PKey::DSA.new(256) assert(key.private?) key2 = OpenSSL::PKey::DSA.new(key.to_der) assert(key2.private?) key3 = key.public_key assert(!key3.private?) key4 = OpenSSL::PKey::DSA.new(key3.to_der) assert(!key4.private?) end def test_new key = OpenSSL::PKey::DSA.new 256 pem = key.public_key.to_pem OpenSSL::PKey::DSA.new pem if $0 == __FILE__ assert_nothing_raised { key = OpenSSL::PKey::DSA.new 2048 } end end def test_new_break assert_nil(OpenSSL::PKey::DSA.new(512) { break }) assert_raise(RuntimeError) do OpenSSL::PKey::DSA.new(512) { raise } end end def test_sign_verify dsa512 = Fixtures.pkey("dsa512") data = "Sign me!" if defined?(OpenSSL::Digest::DSS1) signature = dsa512.sign(OpenSSL::Digest::DSS1.new, data) assert_equal true, dsa512.verify(OpenSSL::Digest::DSS1.new, signature, data) end return unless openssl?(1, 0, 0) signature = dsa512.sign("SHA1", data) assert_equal true, dsa512.verify("SHA1", signature, data) signature0 = (<<~'end;').unpack("m")[0] MCwCFH5h40plgU5Fh0Z4wvEEpz0eE9SnAhRPbkRB8ggsN/vsSEYMXvJwjGg/ 6g== end; assert_equal true, dsa512.verify("SHA256", signature0, data) signature1 = signature0.succ assert_equal false, dsa512.verify("SHA256", signature1, data) end def test_sys_sign_verify key = Fixtures.pkey("dsa256") data = 'Sign me!' digest = OpenSSL::Digest::SHA1.digest(data) sig = key.syssign(digest) assert(key.sysverify(digest, sig)) end def test_DSAPrivateKey # OpenSSL DSAPrivateKey format; similar to RSAPrivateKey dsa512 = Fixtures.pkey("dsa512") asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(0), OpenSSL::ASN1::Integer(dsa512.p), OpenSSL::ASN1::Integer(dsa512.q), OpenSSL::ASN1::Integer(dsa512.g), OpenSSL::ASN1::Integer(dsa512.pub_key), OpenSSL::ASN1::Integer(dsa512.priv_key) ]) key = OpenSSL::PKey::DSA.new(asn1.to_der) assert_predicate key, :private? assert_same_dsa dsa512, key pem = <<~EOF -----BEGIN DSA PRIVATE KEY----- MIH4AgEAAkEA5lB4GvEwjrsMlGDqGsxrbqeFRh6o9OWt6FgTYiEEHaOYhkIxv0Ok RZPDNwOG997mDjBnvDJ1i56OmS3MbTnovwIVAJgub/aDrSDB4DZGH7UyarcaGy6D AkB9HdFw/3td8K4l1FZHv7TCZeJ3ZLb7dF3TWoGUP003RCqoji3/lHdKoVdTQNuR S/m6DlCwhjRjiQ/lBRgCLCcaAkEAjN891JBjzpMj4bWgsACmMggFf57DS0Ti+5++ Q1VB8qkJN7rA7/2HrCR3gTsWNb1YhAsnFsoeRscC+LxXoXi9OAIUBG98h4tilg6S 55jreJD3Se3slps= -----END DSA PRIVATE KEY----- EOF key = OpenSSL::PKey::DSA.new(pem) assert_same_dsa dsa512, key assert_equal asn1.to_der, dsa512.to_der assert_equal pem, dsa512.export end def test_DSAPrivateKey_encrypted # key = abcdef dsa512 = Fixtures.pkey("dsa512") pem = <<~EOF -----BEGIN DSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-128-CBC,F8BB7BFC7EAB9118AC2E3DA16C8DB1D9 D2sIzsM9MLXBtlF4RW42u2GB9gX3HQ3prtVIjWPLaKBYoToRUiv8WKsjptfZuLSB 74ZPdMS7VITM+W1HIxo/tjS80348Cwc9ou8H/E6WGat8ZUk/igLOUEII+coQS6qw QpuLMcCIavevX0gjdjEIkojBB81TYDofA1Bp1z1zDI/2Zhw822xapI79ZF7Rmywt OSyWzFaGipgDpdFsGzvT6//z0jMr0AuJVcZ0VJ5lyPGQZAeVBlbYEI4T72cC5Cz7 XvLiaUtum6/sASD2PQqdDNpgx/WA6Vs1Po2kIUQIM5TIwyJI0GdykZcYm6xIK/ta Wgx6c8K+qBAIVrilw3EWxw== -----END DSA PRIVATE KEY----- EOF key = OpenSSL::PKey::DSA.new(pem, "abcdef") assert_same_dsa dsa512, key key = OpenSSL::PKey::DSA.new(pem) { "abcdef" } assert_same_dsa dsa512, key cipher = OpenSSL::Cipher.new("aes-128-cbc") exported = dsa512.to_pem(cipher, "abcdef\0\1") assert_same_dsa dsa512, OpenSSL::PKey::DSA.new(exported, "abcdef\0\1") assert_raise(OpenSSL::PKey::DSAError) { OpenSSL::PKey::DSA.new(exported, "abcdef") } end def test_PUBKEY dsa512 = Fixtures.pkey("dsa512") asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::ObjectId("DSA"), OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(dsa512.p), OpenSSL::ASN1::Integer(dsa512.q), OpenSSL::ASN1::Integer(dsa512.g) ]) ]), OpenSSL::ASN1::BitString( OpenSSL::ASN1::Integer(dsa512.pub_key).to_der ) ]) key = OpenSSL::PKey::DSA.new(asn1.to_der) assert_not_predicate key, :private? assert_same_dsa dup_public(dsa512), key pem = <<~EOF -----BEGIN PUBLIC KEY----- MIHxMIGoBgcqhkjOOAQBMIGcAkEA5lB4GvEwjrsMlGDqGsxrbqeFRh6o9OWt6FgT YiEEHaOYhkIxv0OkRZPDNwOG997mDjBnvDJ1i56OmS3MbTnovwIVAJgub/aDrSDB 4DZGH7UyarcaGy6DAkB9HdFw/3td8K4l1FZHv7TCZeJ3ZLb7dF3TWoGUP003RCqo ji3/lHdKoVdTQNuRS/m6DlCwhjRjiQ/lBRgCLCcaA0QAAkEAjN891JBjzpMj4bWg sACmMggFf57DS0Ti+5++Q1VB8qkJN7rA7/2HrCR3gTsWNb1YhAsnFsoeRscC+LxX oXi9OA== -----END PUBLIC KEY----- EOF key = OpenSSL::PKey::DSA.new(pem) assert_same_dsa dup_public(dsa512), key assert_equal asn1.to_der, dup_public(dsa512).to_der assert_equal pem, dup_public(dsa512).export end def test_read_DSAPublicKey_pem # TODO: where is the standard? PKey::DSA.new can read only PEM p = 12260055936871293565827712385212529106400444521449663325576634579961635627321079536132296996623400607469624537382977152381984332395192110731059176842635699 q = 979494906553787301107832405790107343409973851677 g = 3731695366899846297271147240305742456317979984190506040697507048095553842519347835107669437969086119948785140453492839427038591924536131566350847469993845 y = 10505239074982761504240823422422813362721498896040719759460296306305851824586095328615844661273887569281276387605297130014564808567159023649684010036304695 pem = <<-EOF -----BEGIN DSA PUBLIC KEY----- MIHfAkEAyJSJ+g+P/knVcgDwwTzC7Pwg/pWs2EMd/r+lYlXhNfzg0biuXRul8VR4 VUC/phySExY0PdcqItkR/xYAYNMbNwJBAOoV57X0FxKO/PrNa/MkoWzkCKV/hzhE p0zbFdsicw+hIjJ7S6Sd/FlDlo89HQZ2FuvWJ6wGLM1j00r39+F2qbMCFQCrkhIX SG+is37hz1IaBeEudjB2HQJAR0AloavBvtsng8obsjLb7EKnB+pSeHr/BdIQ3VH7 fWLOqqkzFeRrYMDzUpl36XktY6Yq8EJYlW9pCMmBVNy/dQ== -----END DSA PUBLIC KEY----- EOF key = OpenSSL::PKey::DSA.new(pem) assert(key.public?) assert(!key.private?) assert_equal(p, key.p) assert_equal(q, key.q) assert_equal(g, key.g) assert_equal(y, key.pub_key) assert_equal(nil, key.priv_key) end def test_dup key = OpenSSL::PKey::DSA.new(256) key2 = key.dup assert_equal key.params, key2.params key2.set_pqg(key2.p + 1, key2.q, key2.g) assert_not_equal key.params, key2.params end private def assert_same_dsa(expected, key) check_component(expected, key, [:p, :q, :g, :pub_key, :priv_key]) end end end openssl-2.0.9/test/test_pkey_ec.rb000066400000000000000000000260361336157045000172020ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) && defined?(OpenSSL::PKey::EC) class OpenSSL::TestEC < OpenSSL::PKeyTestCase def test_ec_key builtin_curves = OpenSSL::PKey::EC.builtin_curves assert_not_empty builtin_curves builtin_curves.each do |curve_name, comment| # Oakley curves and X25519 are not suitable for signing and causes # FIPS-selftest failure on some environment, so skip for now. next if ["Oakley", "X25519"].any? { |n| curve_name.start_with?(n) } key = OpenSSL::PKey::EC.new(curve_name) key.generate_key! assert_predicate key, :private? assert_predicate key, :public? assert_nothing_raised { key.check_key } end key1 = OpenSSL::PKey::EC.new("prime256v1").generate_key! key2 = OpenSSL::PKey::EC.new key2.group = key1.group key2.private_key = key1.private_key key2.public_key = key1.public_key assert_equal key1.to_der, key2.to_der key3 = OpenSSL::PKey::EC.new(key1) assert_equal key1.to_der, key3.to_der key4 = OpenSSL::PKey::EC.new(key1.to_der) assert_equal key1.to_der, key4.to_der key5 = key1.dup assert_equal key1.to_der, key5.to_der key_tmp = OpenSSL::PKey::EC.new("prime256v1").generate_key! key5.private_key = key_tmp.private_key key5.public_key = key_tmp.public_key assert_not_equal key1.to_der, key5.to_der end def test_generate assert_raise(OpenSSL::PKey::ECError) { OpenSSL::PKey::EC.generate("non-existent") } g = OpenSSL::PKey::EC::Group.new("prime256v1") ec = OpenSSL::PKey::EC.generate(g) assert_equal(true, ec.private?) ec = OpenSSL::PKey::EC.generate("prime256v1") assert_equal(true, ec.private?) end def test_check_key key = OpenSSL::PKey::EC.new("prime256v1").generate_key! assert_equal(true, key.check_key) assert_equal(true, key.private?) assert_equal(true, key.public?) key2 = OpenSSL::PKey::EC.new(key.group) assert_equal(false, key2.private?) assert_equal(false, key2.public?) key2.public_key = key.public_key assert_equal(false, key2.private?) assert_equal(true, key2.public?) key2.private_key = key.private_key assert_equal(true, key2.private?) assert_equal(true, key2.public?) assert_equal(true, key2.check_key) key2.private_key += 1 assert_raise(OpenSSL::PKey::ECError) { key2.check_key } end def test_sign_verify p256 = Fixtures.pkey("p256") data = "Sign me!" signature = p256.sign("SHA1", data) assert_equal true, p256.verify("SHA1", signature, data) signature0 = (<<~'end;').unpack("m")[0] MEQCIEOTY/hD7eI8a0qlzxkIt8LLZ8uwiaSfVbjX2dPAvN11AiAQdCYx56Fq QdBp1B4sxJoA8jvODMMklMyBKVmudboA6A== end; assert_equal true, p256.verify("SHA256", signature0, data) signature1 = signature0.succ assert_equal false, p256.verify("SHA256", signature1, data) end def test_dsa_sign_verify data1 = "foo" data2 = "bar" key = OpenSSL::PKey::EC.new("prime256v1").generate_key! sig = key.dsa_sign_asn1(data1) assert_equal true, key.dsa_verify_asn1(data1, sig) assert_equal false, key.dsa_verify_asn1(data2, sig) end def test_dsa_sign_asn1_FIPS186_3 key = OpenSSL::PKey::EC.new("prime256v1").generate_key! size = key.group.order.num_bits / 8 + 1 dgst = (1..size).to_a.pack('C*') begin sig = key.dsa_sign_asn1(dgst) # dgst is auto-truncated according to FIPS186-3 after openssl-0.9.8m assert(key.dsa_verify_asn1(dgst + "garbage", sig)) rescue OpenSSL::PKey::ECError => e # just an exception for longer dgst before openssl-0.9.8m assert_equal('ECDSA_sign: data too large for key size', e.message) # no need to do following tests return end end def test_dh_compute_key key_a = OpenSSL::PKey::EC.new("prime256v1").generate_key! key_b = OpenSSL::PKey::EC.new(key_a.group).generate_key! pub_a = key_a.public_key pub_b = key_b.public_key a = key_a.dh_compute_key(pub_b) b = key_b.dh_compute_key(pub_a) assert_equal a, b end def test_ECPrivateKey p256 = Fixtures.pkey("p256") asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(1), OpenSSL::ASN1::OctetString(p256.private_key.to_s(2)), OpenSSL::ASN1::ASN1Data.new( [OpenSSL::ASN1::ObjectId("prime256v1")], 0, :CONTEXT_SPECIFIC ), OpenSSL::ASN1::ASN1Data.new( [OpenSSL::ASN1::BitString(p256.public_key.to_bn.to_s(2))], 1, :CONTEXT_SPECIFIC ) ]) key = OpenSSL::PKey::EC.new(asn1.to_der) assert_predicate key, :private? assert_same_ec p256, key pem = <<~EOF -----BEGIN EC PRIVATE KEY----- MHcCAQEEIID49FDqcf1O1eO8saTgG70UbXQw9Fqwseliit2aWhH1oAoGCCqGSM49 AwEHoUQDQgAEFglk2c+oVUIKQ64eZG9bhLNPWB7lSZ/ArK41eGy5wAzU/0G51Xtt CeBUl+MahZtn9fO1JKdF4qJmS39dXnpENg== -----END EC PRIVATE KEY----- EOF key = OpenSSL::PKey::EC.new(pem) assert_same_ec p256, key assert_equal asn1.to_der, p256.to_der assert_equal pem, p256.export end def test_ECPrivateKey_encrypted p256 = Fixtures.pkey("p256") # key = abcdef pem = <<~EOF -----BEGIN EC PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-128-CBC,85743EB6FAC9EA76BF99D9328AFD1A66 nhsP1NHxb53aeZdzUe9umKKyr+OIwQq67eP0ONM6E1vFTIcjkDcFLR6PhPFufF4m y7E2HF+9uT1KPQhlE+D63i1m1Mvez6PWfNM34iOQp2vEhaoHHKlR3c43lLyzaZDI 0/dGSU5SzFG+iT9iFXCwCvv+bxyegkBOyALFje1NAsM= -----END EC PRIVATE KEY----- EOF key = OpenSSL::PKey::EC.new(pem, "abcdef") assert_same_ec p256, key key = OpenSSL::PKey::EC.new(pem) { "abcdef" } assert_same_ec p256, key cipher = OpenSSL::Cipher.new("aes-128-cbc") exported = p256.to_pem(cipher, "abcdef\0\1") assert_same_ec p256, OpenSSL::PKey::EC.new(exported, "abcdef\0\1") assert_raise(OpenSSL::PKey::ECError) { OpenSSL::PKey::EC.new(exported, "abcdef") } end def test_PUBKEY p256 = Fixtures.pkey("p256") asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::ObjectId("id-ecPublicKey"), OpenSSL::ASN1::ObjectId("prime256v1") ]), OpenSSL::ASN1::BitString( p256.public_key.to_bn.to_s(2) ) ]) key = OpenSSL::PKey::EC.new(asn1.to_der) assert_not_predicate key, :private? assert_same_ec dup_public(p256), key pem = <<~EOF -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFglk2c+oVUIKQ64eZG9bhLNPWB7l SZ/ArK41eGy5wAzU/0G51XttCeBUl+MahZtn9fO1JKdF4qJmS39dXnpENg== -----END PUBLIC KEY----- EOF key = OpenSSL::PKey::EC.new(pem) assert_same_ec dup_public(p256), key assert_equal asn1.to_der, dup_public(p256).to_der assert_equal pem, dup_public(p256).export end def test_ec_group group1 = OpenSSL::PKey::EC::Group.new("prime256v1") key1 = OpenSSL::PKey::EC.new(group1) assert_equal group1, key1.group group2 = OpenSSL::PKey::EC::Group.new(group1) assert_equal group1.to_der, group2.to_der assert_equal group1, group2 group2.asn1_flag ^=OpenSSL::PKey::EC::NAMED_CURVE assert_not_equal group1.to_der, group2.to_der assert_equal group1, group2 group3 = group1.dup assert_equal group1.to_der, group3.to_der assert group1.asn1_flag & OpenSSL::PKey::EC::NAMED_CURVE # our default der = group1.to_der group4 = OpenSSL::PKey::EC::Group.new(der) group1.point_conversion_form = group4.point_conversion_form = :uncompressed assert_equal :uncompressed, group1.point_conversion_form assert_equal :uncompressed, group4.point_conversion_form assert_equal group1, group4 assert_equal group1.curve_name, group4.curve_name assert_equal group1.generator.to_bn, group4.generator.to_bn assert_equal group1.order, group4.order assert_equal group1.cofactor, group4.cofactor assert_equal group1.seed, group4.seed assert_equal group1.degree, group4.degree end def test_ec_point group = OpenSSL::PKey::EC::Group.new("prime256v1") key = OpenSSL::PKey::EC.new(group).generate_key! point = key.public_key point2 = OpenSSL::PKey::EC::Point.new(group, point.to_bn) assert_equal point, point2 assert_equal point.to_bn, point2.to_bn point2.invert! assert_not_equal point.to_bn, point2.to_bn begin group = OpenSSL::PKey::EC::Group.new(:GFp, 17, 2, 2) group.point_conversion_form = :uncompressed generator = OpenSSL::PKey::EC::Point.new(group, 0x040501.to_bn) group.set_generator(generator, 19, 1) point = OpenSSL::PKey::EC::Point.new(group, 0x040603.to_bn) rescue OpenSSL::PKey::EC::Group::Error pend "Patched OpenSSL rejected curve" if /unsupported field/ =~ $!.message raise end assert_equal 0x040603.to_bn, point.to_bn(:uncompressed) assert_equal 0x0306.to_bn, point.to_bn(:compressed) assert_equal 0x070603.to_bn, point.to_bn(:hybrid) assert_equal 0x040603.to_bn, point.to_bn assert_equal true, point.on_curve? point.invert! # 8.5 assert_equal 0x04060E.to_bn, point.to_bn assert_equal true, point.on_curve? assert_equal false, point.infinity? point.set_to_infinity! assert_equal true, point.infinity? assert_equal 0.to_bn, point.to_bn assert_equal true, point.on_curve? end def test_ec_point_mul begin # y^2 = x^3 + 2x + 2 over F_17 # generator is (5, 1) group = OpenSSL::PKey::EC::Group.new(:GFp, 17, 2, 2) group.point_conversion_form = :uncompressed gen = OpenSSL::PKey::EC::Point.new(group, OpenSSL::BN.new("040501", 16)) group.set_generator(gen, 19, 1) # 3 * (6, 3) = (16, 13) point_a = OpenSSL::PKey::EC::Point.new(group, OpenSSL::BN.new("040603", 16)) result_a1 = point_a.mul(3) assert_equal("04100D", result_a1.to_bn.to_s(16)) # 3 * (6, 3) + 3 * (5, 1) = (7, 6) result_a2 = point_a.mul(3, 3) assert_equal("040706", result_a2.to_bn.to_s(16)) # 3 * point_a = 3 * (6, 3) = (16, 13) result_b1 = point_a.mul([3], []) assert_equal("04100D", result_b1.to_bn.to_s(16)) # 3 * point_a + 2 * point_a = 3 * (6, 3) + 2 * (6, 3) = (7, 11) result_b1 = point_a.mul([3, 2], [point_a]) assert_equal("04070B", result_b1.to_bn.to_s(16)) # 3 * point_a + 5 * point_a.group.generator = 3 * (6, 3) + 5 * (5, 1) = (13, 10) result_b1 = point_a.mul([3], [], 5) assert_equal("040D0A", result_b1.to_bn.to_s(16)) rescue OpenSSL::PKey::EC::Group::Error # CentOS patches OpenSSL to reject curves defined over Fp where p < 256 bits raise if $!.message !~ /unsupported field/ end p256_key = Fixtures.pkey("p256") p256_g = p256_key.group assert_equal(p256_key.public_key, p256_g.generator.mul(p256_key.private_key)) # invalid argument point = p256_key.public_key assert_raise(TypeError) { point.mul(nil) } assert_raise(ArgumentError) { point.mul([1], [point]) } assert_raise(TypeError) { point.mul([1], nil) } assert_raise(TypeError) { point.mul([nil], []) } end # test Group: asn1_flag, point_conversion private def assert_same_ec(expected, key) check_component(expected, key, [:group, :public_key, :private_key]) end end end openssl-2.0.9/test/test_pkey_rsa.rb000066400000000000000000000223631336157045000173770ustar00rootroot00000000000000# frozen_string_literal: false require_relative "utils" if defined?(OpenSSL::TestUtils) class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase def test_padding key = OpenSSL::PKey::RSA.new(512, 3) # Need right size for raw mode plain0 = "x" * (512/8) cipher = key.private_encrypt(plain0, OpenSSL::PKey::RSA::NO_PADDING) plain1 = key.public_decrypt(cipher, OpenSSL::PKey::RSA::NO_PADDING) assert_equal(plain0, plain1) # Need smaller size for pkcs1 mode plain0 = "x" * (512/8 - 11) cipher1 = key.private_encrypt(plain0, OpenSSL::PKey::RSA::PKCS1_PADDING) plain1 = key.public_decrypt(cipher1, OpenSSL::PKey::RSA::PKCS1_PADDING) assert_equal(plain0, plain1) cipherdef = key.private_encrypt(plain0) # PKCS1_PADDING is default plain1 = key.public_decrypt(cipherdef) assert_equal(plain0, plain1) assert_equal(cipher1, cipherdef) # Failure cases assert_raise(ArgumentError){ key.private_encrypt() } assert_raise(ArgumentError){ key.private_encrypt("hi", 1, nil) } assert_raise(OpenSSL::PKey::RSAError){ key.private_encrypt(plain0, 666) } end def test_private key = OpenSSL::PKey::RSA.new(512, 3) assert(key.private?) key2 = OpenSSL::PKey::RSA.new(key.to_der) assert(key2.private?) key3 = key.public_key assert(!key3.private?) key4 = OpenSSL::PKey::RSA.new(key3.to_der) assert(!key4.private?) end def test_new key = OpenSSL::PKey::RSA.new 512 pem = key.public_key.to_pem OpenSSL::PKey::RSA.new pem assert_equal([], OpenSSL.errors) end def test_new_exponent_default assert_equal(65537, OpenSSL::PKey::RSA.new(512).e) end def test_new_with_exponent 1.upto(30) do |idx| e = (2 ** idx) + 1 key = OpenSSL::PKey::RSA.new(512, e) assert_equal(e, key.e) end end def test_generate key = OpenSSL::PKey::RSA.generate(512, 17) assert_equal 512, key.n.num_bits assert_equal 17, key.e assert_not_nil key.d end def test_new_break assert_nil(OpenSSL::PKey::RSA.new(1024) { break }) assert_raise(RuntimeError) do OpenSSL::PKey::RSA.new(1024) { raise } end end def test_sign_verify rsa1024 = Fixtures.pkey("rsa1024") data = "Sign me!" signature = rsa1024.sign("SHA1", data) assert_equal true, rsa1024.verify("SHA1", signature, data) signature0 = (<<~'end;').unpack("m")[0] oLCgbprPvfhM4pjFQiDTFeWI9Sk+Og7Nh9TmIZ/xSxf2CGXQrptlwo7NQ28+ WA6YQo8jPH4hSuyWIM4Gz4qRYiYRkl5TDMUYob94zm8Si1HxEiS9354tzvqS zS8MLW2BtNPuTubMxTItHGTnOzo9sUg0LAHVFt8kHG2NfKAw/gQ= end; assert_equal true, rsa1024.verify("SHA256", signature0, data) signature1 = signature0.succ assert_equal false, rsa1024.verify("SHA256", signature1, data) end def test_digest_state_irrelevant_sign key = Fixtures.pkey("rsa1024") digest1 = OpenSSL::Digest::SHA1.new digest2 = OpenSSL::Digest::SHA1.new data = 'Sign me!' digest1 << 'Change state of digest1' sig1 = key.sign(digest1, data) sig2 = key.sign(digest2, data) assert_equal(sig1, sig2) end def test_digest_state_irrelevant_verify key = Fixtures.pkey("rsa1024") digest1 = OpenSSL::Digest::SHA1.new digest2 = OpenSSL::Digest::SHA1.new data = 'Sign me!' sig = key.sign(digest1, data) digest1.reset digest1 << 'Change state of digest1' assert(key.verify(digest1, sig, data)) assert(key.verify(digest2, sig, data)) end def test_verify_empty_rsa rsa = OpenSSL::PKey::RSA.new assert_raise(OpenSSL::PKey::PKeyError, "[Bug #12783]") { rsa.verify("SHA1", "a", "b") } end def test_RSAPrivateKey rsa1024 = Fixtures.pkey("rsa1024") asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(0), OpenSSL::ASN1::Integer(rsa1024.n), OpenSSL::ASN1::Integer(rsa1024.e), OpenSSL::ASN1::Integer(rsa1024.d), OpenSSL::ASN1::Integer(rsa1024.p), OpenSSL::ASN1::Integer(rsa1024.q), OpenSSL::ASN1::Integer(rsa1024.dmp1), OpenSSL::ASN1::Integer(rsa1024.dmq1), OpenSSL::ASN1::Integer(rsa1024.iqmp) ]) key = OpenSSL::PKey::RSA.new(asn1.to_der) assert_predicate key, :private? assert_same_rsa rsa1024, key pem = <<~EOF -----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7Cx aKPERYHsk4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/ Q3geLv8ZD9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQAB AoGBAKSl/MQarye1yOysqX6P8fDFQt68VvtXkNmlSiKOGuzyho0M+UVSFcs6k1L0 maDE25AMZUiGzuWHyaU55d7RXDgeskDMakD1v6ZejYtxJkSXbETOTLDwUWTn618T gnb17tU1jktUtU67xK/08i/XodlgnQhs6VoHTuCh3Hu77O6RAkEA7+gxqBuZR572 74/akiW/SuXm0SXPEviyO1MuSRwtI87B02D0qgV8D1UHRm4AhMnJ8MCs1809kMQE JiQUCrp9mQJBANlt2ngBO14us6NnhuAseFDTBzCHXwUUu1YKHpMMmxpnGqaldGgX sOZB3lgJsT9VlGf3YGYdkLTNVbogQKlKpB8CQQDiSwkb4vyQfDe8/NpU5Not0fII 8jsDUCb+opWUTMmfbxWRR3FBNu8wnym/m19N4fFj8LqYzHX4KY0oVPu6qvJxAkEA wa5snNekFcqONLIE4G5cosrIrb74sqL8GbGb+KuTAprzj5z1K8Bm0UW9lTjVDjDi qRYgZfZSL+x1P/54+xTFSwJAY1FxA/N3QPCXCjPh5YqFxAMQs2VVYTfg+t0MEcJD dPMQD5JX6g5HKnHFg2mZtoXQrWmJSn7p8GJK8yNTopEErA== -----END RSA PRIVATE KEY----- EOF key = OpenSSL::PKey::RSA.new(pem) assert_same_rsa rsa1024, key assert_equal asn1.to_der, rsa1024.to_der assert_equal pem, rsa1024.export end def test_RSAPrivateKey_encrypted rsa1024 = Fixtures.pkey("rsa1024") # key = abcdef pem = <<~EOF -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0 zgJniZZQfvv8TFx3LzV6zhAQVayvQVZlAYqFq2yWbbxzF7C+IBhKQle9IhUQ9j/y /jkvol550LS8vZ7TX5WxyDLe12cdqzEvpR6jf3NbxiNysOCxwG4ErhaZGP+krcoB ObuL0nvls/+3myy5reKEyy22+0GvTDjaChfr+FwJjXMG+IBCLscYdgZC1LQL6oAn 9xY5DH3W7BW4wR5ttxvtN32TkfVQh8xi3jrLrduUh+hV8DTiAiLIhv0Vykwhep2p WZA+7qbrYaYM8GLLgLrb6LfBoxeNxAEKiTpl1quFkm+Hk1dKq0EhVnxHf92x0zVF jRGZxAMNcrlCoE4f5XK45epVZSZvihdo1k73GPbp84aZ5P/xlO4OwZ3i4uCQXynl jE9c+I+4rRWKyPz9gkkqo0+teJL8ifeKt/3ab6FcdA0aArynqmsKJMktxmNu83We YVGEHZPeOlyOQqPvZqWsLnXQUfg54OkbuV4/4mWSIzxFXdFy/AekSeJugpswMXqn oNck4qySNyfnlyelppXyWWwDfVus9CVAGZmJQaJExHMT/rQFRVchlmY0Ddr5O264 gcjv90o1NBOc2fNcqjivuoX7ROqys4K/YdNQ1HhQ7usJghADNOtuLI8ZqMh9akXD Eqp6Ne97wq1NiJj0nt3SJlzTnOyTjzrTe0Y+atPkVKp7SsjkATMI9JdhXwGhWd7a qFVl0owZiDasgEhyG2K5L6r+yaJLYkPVXZYC/wtWC3NEchnDWZGQcXzB4xROCQkD OlWNYDkPiZioeFkA3/fTMvG4moB2Pp9Q4GU5fJ6k43Ccu1up8dX/LumZb4ecg5/x -----END RSA PRIVATE KEY----- EOF key = OpenSSL::PKey::RSA.new(pem, "abcdef") assert_same_rsa rsa1024, key key = OpenSSL::PKey::RSA.new(pem) { "abcdef" } assert_same_rsa rsa1024, key cipher = OpenSSL::Cipher.new("aes-128-cbc") exported = rsa1024.to_pem(cipher, "abcdef\0\1") assert_same_rsa rsa1024, OpenSSL::PKey::RSA.new(exported, "abcdef\0\1") assert_raise(OpenSSL::PKey::RSAError) { OpenSSL::PKey::RSA.new(exported, "abcdef") } end def test_RSAPublicKey rsa1024 = Fixtures.pkey("rsa1024") asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(rsa1024.n), OpenSSL::ASN1::Integer(rsa1024.e) ]) key = OpenSSL::PKey::RSA.new(asn1.to_der) assert_not_predicate key, :private? assert_same_rsa dup_public(rsa1024), key pem = <<~EOF -----BEGIN RSA PUBLIC KEY----- MIGJAoGBAMvCxLDUQKc+1P4+Q6AeFwYDvWfALb+cvzlUEadGoPE6qNWHsLFoo8RF geyTgE8KQTduu1OE9Zz2SMcRBDu5/1jWtsLPSVrI2ofLLBARUsWanVyki39DeB4u /xkP2mKGjAokPIwOI3oCthSZlzO9bj3voxTf6XngTqUX8l8URTmHAgMBAAE= -----END RSA PUBLIC KEY----- EOF key = OpenSSL::PKey::RSA.new(pem) assert_same_rsa dup_public(rsa1024), key end def test_PUBKEY rsa1024 = Fixtures.pkey("rsa1024") asn1 = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::ObjectId("rsaEncryption"), OpenSSL::ASN1::Null(nil) ]), OpenSSL::ASN1::BitString( OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Integer(rsa1024.n), OpenSSL::ASN1::Integer(rsa1024.e) ]).to_der ) ]) key = OpenSSL::PKey::RSA.new(asn1.to_der) assert_not_predicate key, :private? assert_same_rsa dup_public(rsa1024), key pem = <<~EOF -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLwsSw1ECnPtT+PkOgHhcGA71n wC2/nL85VBGnRqDxOqjVh7CxaKPERYHsk4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbC z0layNqHyywQEVLFmp1cpIt/Q3geLv8ZD9pihowKJDyMDiN6ArYUmZczvW4976MU 3+l54E6lF/JfFEU5hwIDAQAB -----END PUBLIC KEY----- EOF key = OpenSSL::PKey::RSA.new(pem) assert_same_rsa dup_public(rsa1024), key assert_equal asn1.to_der, dup_public(rsa1024).to_der assert_equal pem, dup_public(rsa1024).export end def test_pem_passwd key = Fixtures.pkey("rsa1024") pem3c = key.to_pem("aes-128-cbc", "key") assert_match (/ENCRYPTED/), pem3c assert_equal key.to_der, OpenSSL::PKey.read(pem3c, "key").to_der assert_equal key.to_der, OpenSSL::PKey.read(pem3c) { "key" }.to_der assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.read(pem3c) { nil } } end def test_dup key = Fixtures.pkey("rsa1024") key2 = key.dup assert_equal key.params, key2.params key2.set_key(key2.n, 3, key2.d) assert_not_equal key.params, key2.params end private def assert_same_rsa(expected, key) check_component(expected, key, [:n, :e, :d, :p, :q, :dmp1, :dmq1, :iqmp]) end end end openssl-2.0.9/test/test_random.rb000066400000000000000000000010221336157045000170270ustar00rootroot00000000000000# frozen_string_literal: false require_relative "utils" if defined?(OpenSSL::TestUtils) class OpenSSL::TestRandom < OpenSSL::TestCase def test_random_bytes assert_equal("", OpenSSL::Random.random_bytes(0)) assert_equal(12, OpenSSL::Random.random_bytes(12).bytesize) end def test_pseudo_bytes # deprecated as of OpenSSL 1.1.0 assert_equal("", OpenSSL::Random.pseudo_bytes(0)) assert_equal(12, OpenSSL::Random.pseudo_bytes(12).bytesize) end if OpenSSL::Random.methods.include?(:pseudo_bytes) end end openssl-2.0.9/test/test_ssl.rb000066400000000000000000001245041336157045000163630ustar00rootroot00000000000000# frozen_string_literal: false require_relative "utils" if defined?(OpenSSL::TestUtils) class OpenSSL::TestSSL < OpenSSL::SSLTestCase def test_ctx_options ctx = OpenSSL::SSL::SSLContext.new assert (OpenSSL::SSL::OP_ALL & ctx.options) == OpenSSL::SSL::OP_ALL, "OP_ALL is set by default" ctx.options = 4 assert_equal 4, ctx.options & 4 if ctx.options != 4 pend "SSL_CTX_set_options() seems to be modified by distributor" end ctx.options = nil assert_equal OpenSSL::SSL::OP_ALL, ctx.options assert_equal true, ctx.setup assert_predicate ctx, :frozen? assert_equal nil, ctx.setup end def test_ssl_with_server_cert ctx_proc = -> ctx { ctx.cert = @svr_cert ctx.key = @svr_key ctx.extra_chain_cert = [@ca_cert] } server_proc = -> (ctx, ssl) { assert_equal @svr_cert.to_der, ssl.cert.to_der assert_equal nil, ssl.peer_cert readwrite_loop(ctx, ssl) } start_server(ctx_proc: ctx_proc, server_proc: server_proc) { |port| begin sock = TCPSocket.new("127.0.0.1", port) ctx = OpenSSL::SSL::SSLContext.new ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.connect assert_equal sock, ssl.io assert_equal nil, ssl.cert assert_equal @svr_cert.to_der, ssl.peer_cert.to_der assert_equal 2, ssl.peer_cert_chain.size assert_equal @svr_cert.to_der, ssl.peer_cert_chain[0].to_der assert_equal @ca_cert.to_der, ssl.peer_cert_chain[1].to_der ssl.puts "abc"; assert_equal "abc\n", ssl.gets ensure ssl&.close sock&.close end } end def test_sysread_and_syswrite start_server { |port| server_connect(port) { |ssl| str = "x" * 100 + "\n" ssl.syswrite(str) newstr = ssl.sysread(str.bytesize) assert_equal(str, newstr) buf = "" ssl.syswrite(str) assert_same buf, ssl.sysread(str.size, buf) assert_equal(str, buf) } } end def test_sync_close start_server { |port| begin sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.connect ssl.puts "abc"; assert_equal "abc\n", ssl.gets ssl.close assert_not_predicate sock, :closed? ensure sock&.close end begin sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.sync_close = true # !! ssl.connect ssl.puts "abc"; assert_equal "abc\n", ssl.gets ssl.close assert_predicate sock, :closed? ensure sock&.close end } end def test_copy_stream start_server do |port| server_connect(port) do |ssl| IO.pipe do |r, w| str = "hello world\n" w.write(str) IO.copy_stream(r, ssl, str.bytesize) IO.copy_stream(ssl, w, str.bytesize) assert_equal str, r.read(str.bytesize) end end end end def test_client_auth_failure vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT start_server(verify_mode: vflag, ignore_listener_error: true) { |port| assert_handshake_error { server_connect(port) { |ssl| ssl.puts("abc"); ssl.gets } } } end def test_client_auth_success vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT start_server(verify_mode: vflag) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.key = @cli_key ctx.cert = @cli_cert server_connect(port, ctx) { |ssl| ssl.puts("foo") assert_equal("foo\n", ssl.gets) } called = nil ctx = OpenSSL::SSL::SSLContext.new ctx.client_cert_cb = Proc.new{ |sslconn| called = true [@cli_cert, @cli_key] } server_connect(port, ctx) { |ssl| assert(called) ssl.puts("foo") assert_equal("foo\n", ssl.gets) } } end def test_client_auth_public_key vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT start_server(verify_mode: vflag, ignore_listener_error: true) do |port| assert_raise(ArgumentError) { ctx = OpenSSL::SSL::SSLContext.new ctx.key = @cli_key.public_key ctx.cert = @cli_cert server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets } } ctx = OpenSSL::SSL::SSLContext.new ctx.client_cert_cb = Proc.new{ |ssl| [@cli_cert, @cli_key.public_key] } assert_handshake_error { server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets } } end end def test_client_ca ctx_proc = Proc.new do |ctx| ctx.client_ca = [@ca_cert] end vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT start_server(verify_mode: vflag, ctx_proc: ctx_proc) { |port| ctx = OpenSSL::SSL::SSLContext.new client_ca_from_server = nil ctx.client_cert_cb = Proc.new do |sslconn| client_ca_from_server = sslconn.client_ca [@cli_cert, @cli_key] end server_connect(port, ctx) { |ssl| assert_equal([@ca], client_ca_from_server) ssl.puts "abc"; assert_equal "abc\n", ssl.gets } } end def test_read_nonblock_without_session EnvUtil.suppress_warning do start_server(start_immediately: false) { |port| sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.sync_close = true assert_equal :wait_readable, ssl.read_nonblock(100, exception: false) ssl.write("abc\n") IO.select [ssl] assert_equal('a', ssl.read_nonblock(1)) assert_equal("bc\n", ssl.read_nonblock(100)) assert_equal :wait_readable, ssl.read_nonblock(100, exception: false) ssl.close } end end def test_starttls server_proc = -> (ctx, ssl) { while line = ssl.gets if line =~ /^STARTTLS$/ ssl.write("x") ssl.flush ssl.accept break end ssl.write(line) end readwrite_loop(ctx, ssl) } EnvUtil.suppress_warning do # read/write on not started session start_server(start_immediately: false, server_proc: server_proc) { |port| begin sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.puts "plaintext" assert_equal "plaintext\n", ssl.gets ssl.puts("STARTTLS") ssl.read(1) ssl.connect ssl.puts "over-tls" assert_equal "over-tls\n", ssl.gets ensure ssl&.close sock&.close end } end end def test_parallel start_server { |port| ssls = [] 10.times{ sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.connect ssl.sync_close = true ssls << ssl } str = "x" * 1000 + "\n" ITERATIONS.times{ ssls.each{|ssl| ssl.puts(str) assert_equal(str, ssl.gets) } } ssls.each{|ssl| ssl.close } } end def test_verify_result start_server(ignore_listener_error: true) { |port| sock = TCPSocket.new("127.0.0.1", port) ctx = OpenSSL::SSL::SSLContext.new ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.sync_close = true begin assert_raise(OpenSSL::SSL::SSLError){ ssl.connect } assert_equal(OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, ssl.verify_result) ensure ssl.close end } start_server { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER ctx.verify_callback = Proc.new do |preverify_ok, store_ctx| store_ctx.error = OpenSSL::X509::V_OK true end server_connect(port, ctx) { |ssl| assert_equal(OpenSSL::X509::V_OK, ssl.verify_result) ssl.puts "abc"; assert_equal "abc\n", ssl.gets } } start_server(ignore_listener_error: true) { |port| sock = TCPSocket.new("127.0.0.1", port) ctx = OpenSSL::SSL::SSLContext.new ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER ctx.verify_callback = Proc.new do |preverify_ok, store_ctx| store_ctx.error = OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION false end ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.sync_close = true begin assert_raise(OpenSSL::SSL::SSLError){ ssl.connect } assert_equal(OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION, ssl.verify_result) ensure ssl.close end } end def test_exception_in_verify_callback_is_ignored start_server(ignore_listener_error: true) { |port| sock = TCPSocket.new("127.0.0.1", port) ctx = OpenSSL::SSL::SSLContext.new ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER ctx.verify_callback = Proc.new do |preverify_ok, store_ctx| store_ctx.error = OpenSSL::X509::V_OK raise RuntimeError end ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.sync_close = true begin EnvUtil.suppress_warning do # SSLError, not RuntimeError assert_raise(OpenSSL::SSL::SSLError) { ssl.connect } end assert_equal(OpenSSL::X509::V_ERR_CERT_REJECTED, ssl.verify_result) ensure ssl.close end } end def test_sslctx_set_params ctx = OpenSSL::SSL::SSLContext.new ctx.set_params assert_equal OpenSSL::SSL::VERIFY_PEER, ctx.verify_mode ciphers_names = ctx.ciphers.collect{|v, _, _, _| v } assert ciphers_names.all?{|v| /A(EC)?DH/ !~ v }, "anon ciphers are disabled" assert ciphers_names.all?{|v| /(RC4|MD5|EXP|DES(?!-EDE|-CBC3))/ !~ v }, "weak ciphers are disabled" assert_equal 0, ctx.options & OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_NO_COMPRESSION) # >= 1.0.0 assert_equal OpenSSL::SSL::OP_NO_COMPRESSION, ctx.options & OpenSSL::SSL::OP_NO_COMPRESSION end end def test_post_connect_check_with_anon_ciphers pend "TLS 1.2 is not supported" unless tls12_supported? ctx_proc = -> ctx { ctx.ssl_version = :TLSv1_2 ctx.ciphers = "aNULL" ctx.security_level = 0 } start_server(ctx_proc: ctx_proc) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.ssl_version = :TLSv1_2 ctx.ciphers = "aNULL" ctx.security_level = 0 server_connect(port, ctx) { |ssl| assert_raise_with_message(OpenSSL::SSL::SSLError, /anonymous cipher suite/i) { ssl.post_connection_check("localhost.localdomain") } } } end def test_post_connection_check sslerr = OpenSSL::SSL::SSLError start_server { |port| server_connect(port) { |ssl| ssl.puts "abc"; assert_equal "abc\n", ssl.gets assert_raise(sslerr){ssl.post_connection_check("localhost.localdomain")} assert_raise(sslerr){ssl.post_connection_check("127.0.0.1")} assert(ssl.post_connection_check("localhost")) assert_raise(sslerr){ssl.post_connection_check("foo.example.com")} cert = ssl.peer_cert assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain")) assert(!OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1")) assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost")) assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com")) } } exts = [ ["keyUsage","keyEncipherment,digitalSignature",true], ["subjectAltName","DNS:localhost.localdomain",false], ["subjectAltName","IP:127.0.0.1",false], ] @svr_cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key) start_server { |port| server_connect(port) { |ssl| ssl.puts "abc"; assert_equal "abc\n", ssl.gets assert(ssl.post_connection_check("localhost.localdomain")) assert(ssl.post_connection_check("127.0.0.1")) assert_raise(sslerr){ssl.post_connection_check("localhost")} assert_raise(sslerr){ssl.post_connection_check("foo.example.com")} cert = ssl.peer_cert assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain")) assert(OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1")) assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost")) assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com")) } } exts = [ ["keyUsage","keyEncipherment,digitalSignature",true], ["subjectAltName","DNS:*.localdomain",false], ] @svr_cert = issue_cert(@svr, @svr_key, 5, exts, @ca_cert, @ca_key) start_server { |port| server_connect(port) { |ssl| ssl.puts "abc"; assert_equal "abc\n", ssl.gets assert(ssl.post_connection_check("localhost.localdomain")) assert_raise(sslerr){ssl.post_connection_check("127.0.0.1")} assert_raise(sslerr){ssl.post_connection_check("localhost")} assert_raise(sslerr){ssl.post_connection_check("foo.example.com")} cert = ssl.peer_cert assert(OpenSSL::SSL.verify_certificate_identity(cert, "localhost.localdomain")) assert(!OpenSSL::SSL.verify_certificate_identity(cert, "127.0.0.1")) assert(!OpenSSL::SSL.verify_certificate_identity(cert, "localhost")) assert(!OpenSSL::SSL.verify_certificate_identity(cert, "foo.example.com")) } } end def test_verify_certificate_identity [true, false].each do |criticality| cert = create_null_byte_SAN_certificate(criticality) assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, 'www.example.com')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, "www.example.com\0.evil.com")) assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.255')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.1')) assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '13::17')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity(cert, '13:0:0:0:0:0:0:17')) end end def test_verify_hostname assert_equal(true, OpenSSL::SSL.verify_hostname("www.example.com", "*.example.com")) assert_equal(false, OpenSSL::SSL.verify_hostname("www.subdomain.example.com", "*.example.com")) end def test_verify_wildcard assert_equal(false, OpenSSL::SSL.verify_wildcard("foo", "x*")) assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "foo")) assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "f*")) assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "*")) assert_equal(false, OpenSSL::SSL.verify_wildcard("abc*bcd", "abcd")) assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "x*")) assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "*--qdk4b9b")) assert_equal(true, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "xn--qdk4b9b")) end # Comments in this test is excerpted from http://tools.ietf.org/html/rfc6125#page-27 def test_post_connection_check_wildcard_san # case-insensitive ASCII comparison # RFC 6125, section 6.4.1 # # "..matching of the reference identifier against the presented identifier # is performed by comparing the set of domain name labels using a # case-insensitive ASCII comparison, as clarified by [DNS-CASE] (e.g., # "WWW.Example.Com" would be lower-cased to "www.example.com" for # comparison purposes) assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:*.example.com'), 'www.example.com')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:*.Example.COM'), 'www.example.com')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:*.example.com'), 'WWW.Example.COM')) # 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). assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:www.*.com'), 'www.example.com')) # 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). assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:*.example.com'), 'foo.example.com')) assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:*.example.com'), 'bar.foo.example.com')) # 3. The client MAY match a presented identifier in which the wildcard # character is not the only character of the label (e.g., # baz*.example.net and *baz.example.net and b*z.example.net would # be taken to match baz1.example.net and foobaz.example.net and # buzz.example.net, respectively). ... assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:baz*.example.com'), 'baz1.example.com')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:*baz.example.com'), 'foobaz.example.com')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:b*z.example.com'), 'buzz.example.com')) # Section 6.4.3 of RFC6125 states that client should NOT match identifier # where wildcard is other than left-most label. # # Also implicitly mentions the wildcard character only in singular form, # and discourages matching against more than one wildcard. # # See RFC 6125, section 7.2, subitem 2. assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:*b*.example.com'), 'abc.example.com')) assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:*b*.example.com'), 'ab.example.com')) assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:*b*.example.com'), 'bc.example.com')) # ... However, the client SHOULD NOT # attempt to match a presented identifier where the wildcard # character is embedded within an A-label or U-label [IDNA-DEFS] of # an internationalized domain name [IDNA-PROTO]. assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:xn*.example.com'), 'xn1ca.example.com')) # part of A-label assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:xn--*.example.com'), 'xn--1ca.example.com')) # part of U-label # dNSName in RFC5280 is an IA5String so U-label should NOT be allowed # regardless of wildcard. # # See Section 7.2 of RFC 5280: # IA5String is limited to the set of ASCII characters. assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_san('DNS:á*.example.com'), 'á1.example.com')) end def test_post_connection_check_wildcard_cn assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('*.example.com'), 'www.example.com')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('*.Example.COM'), 'www.example.com')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('*.example.com'), 'WWW.Example.COM')) assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('www.*.com'), 'www.example.com')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('*.example.com'), 'foo.example.com')) assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('*.example.com'), 'bar.foo.example.com')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('baz*.example.com'), 'baz1.example.com')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('*baz.example.com'), 'foobaz.example.com')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('b*z.example.com'), 'buzz.example.com')) # Section 6.4.3 of RFC6125 states that client should NOT match identifier # where wildcard is other than left-most label. # # Also implicitly mentions the wildcard character only in singular form, # and discourages matching against more than one wildcard. # # See RFC 6125, section 7.2, subitem 2. assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('*b*.example.com'), 'abc.example.com')) assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('*b*.example.com'), 'ab.example.com')) assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('*b*.example.com'), 'bc.example.com')) assert_equal(true, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('xn*.example.com'), 'xn1ca.example.com')) assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('xn--*.example.com'), 'xn--1ca.example.com')) # part of U-label # Subject in RFC5280 states case-insensitive ASCII comparison. # # See Section 7.2 of RFC 5280: # IA5String is limited to the set of ASCII characters. assert_equal(false, OpenSSL::SSL.verify_certificate_identity( create_cert_with_name('á*.example.com'), 'á1.example.com')) end def create_cert_with_san(san) ef = OpenSSL::X509::ExtensionFactory.new cert = OpenSSL::X509::Certificate.new cert.subject = OpenSSL::X509::Name.parse("/DC=some/DC=site/CN=Some Site") ext = ef.create_ext('subjectAltName', san) cert.add_extension(ext) cert end def create_cert_with_name(name) cert = OpenSSL::X509::Certificate.new cert.subject = OpenSSL::X509::Name.new([['DC', 'some'], ['DC', 'site'], ['CN', name]]) cert end # Create NULL byte SAN certificate def create_null_byte_SAN_certificate(critical = false) ef = OpenSSL::X509::ExtensionFactory.new cert = OpenSSL::X509::Certificate.new cert.subject = OpenSSL::X509::Name.parse "/DC=some/DC=site/CN=Some Site" ext = ef.create_ext('subjectAltName', 'DNS:placeholder,IP:192.168.7.1,IP:13::17', critical) ext_asn1 = OpenSSL::ASN1.decode(ext.to_der) san_list_der = ext_asn1.value.reduce(nil) { |memo,val| val.tag == 4 ? val.value : memo } san_list_asn1 = OpenSSL::ASN1.decode(san_list_der) san_list_asn1.value[0].value = "www.example.com\0.evil.com" pos = critical ? 2 : 1 ext_asn1.value[pos].value = san_list_asn1.to_der real_ext = OpenSSL::X509::Extension.new ext_asn1 cert.add_extension(real_ext) cert end def socketpair if defined? UNIXSocket UNIXSocket.pair else Socket.pair(Socket::AF_INET, Socket::SOCK_STREAM, 0) end end def test_tlsext_hostname fooctx = OpenSSL::SSL::SSLContext.new fooctx.tmp_dh_callback = proc { Fixtures.pkey_dh("dh1024") } fooctx.cert = @cli_cert fooctx.key = @cli_key ctx_proc = proc { |ctx| ctx.servername_cb = proc { |ssl, servername| case servername when "foo.example.com" fooctx when "bar.example.com" nil else raise "unreachable" end } } start_server(ctx_proc: ctx_proc) do |port| sock = TCPSocket.new("127.0.0.1", port) begin ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.hostname = "foo.example.com" ssl.connect assert_equal @cli_cert.serial, ssl.peer_cert.serial assert_predicate fooctx, :frozen? ssl.puts "abc"; assert_equal "abc\n", ssl.gets ensure ssl&.close sock.close end sock = TCPSocket.new("127.0.0.1", port) begin ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.hostname = "bar.example.com" ssl.connect assert_equal @svr_cert.serial, ssl.peer_cert.serial ssl.puts "abc"; assert_equal "abc\n", ssl.gets ensure ssl&.close sock.close end end end def test_servername_cb_raises_an_exception_on_unknown_objects hostname = 'example.org' ctx2 = OpenSSL::SSL::SSLContext.new ctx2.cert = @svr_cert ctx2.key = @svr_key ctx2.tmp_dh_callback = proc { Fixtures.pkey_dh("dh1024") } ctx2.servername_cb = lambda { |args| Object.new } sock1, sock2 = socketpair s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) ctx1 = OpenSSL::SSL::SSLContext.new s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1) s1.hostname = hostname t = Thread.new { assert_raise(OpenSSL::SSL::SSLError) do s1.connect end } assert_raise(ArgumentError) do s2.accept end assert t.join ensure sock1.close if sock1 sock2.close if sock2 end def test_verify_hostname_on_connect ctx_proc = proc { |ctx| exts = [ ["keyUsage", "keyEncipherment,digitalSignature", true], ["subjectAltName", "DNS:a.example.com,DNS:*.b.example.com," \ "DNS:c*.example.com,DNS:d.*.example.com"], ] ctx.cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key) ctx.key = @svr_key } start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.verify_hostname = true ctx.cert_store = OpenSSL::X509::Store.new ctx.cert_store.add_cert(@ca_cert) ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER [ ["a.example.com", true], ["A.Example.Com", true], ["x.example.com", false], ["b.example.com", false], ["x.b.example.com", true], ["cx.example.com", true], ["d.x.example.com", false], ].each do |name, expected_ok| begin sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.hostname = name if expected_ok ssl.connect ssl.puts "abc"; assert_equal "abc\n", ssl.gets else assert_handshake_error { ssl.connect } end ensure ssl.close if ssl sock.close if sock end end end end def test_unset_OP_ALL ctx_proc = Proc.new { |ctx| # If OP_DONT_INSERT_EMPTY_FRAGMENTS is not defined, this test is # redundant because the default options already are equal to OP_ALL. # But it also degrades gracefully, so keep it ctx.options = OpenSSL::SSL::OP_ALL } start_server(ctx_proc: ctx_proc) { |port| server_connect(port) { |ssl| ssl.puts('hello') assert_equal("hello\n", ssl.gets) } } end if OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1) && OpenSSL::SSL::SSLContext::METHODS.include?(:SSLv3) def test_forbid_ssl_v3_for_client ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv3 } start_server_version(:SSLv23, ctx_proc) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.ssl_version = :SSLv3 assert_handshake_error { server_connect(port, ctx) } } end def test_forbid_ssl_v3_from_server start_server_version(:SSLv3) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv3 assert_handshake_error { server_connect(port, ctx) } } end end if OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1_1) && OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1) def test_tls_v1_1 start_server_version(:TLSv1_1) { |port| ctx = OpenSSL::SSL::SSLContext.new(:TLSv1_1) server_connect(port, ctx) { |ssl| assert_equal("TLSv1.1", ssl.ssl_version) } } end def test_forbid_tls_v1_for_client ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1 } start_server_version(:SSLv23, ctx_proc) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.ssl_version = :TLSv1 assert_handshake_error { server_connect(port, ctx) } } end def test_forbid_tls_v1_from_server start_server_version(:TLSv1) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1 assert_handshake_error { server_connect(port, ctx) } } end end if OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1_2) && OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1_1) def test_tls_v1_2 start_server_version(:TLSv1_2) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.ssl_version = :TLSv1_2_client server_connect(port, ctx) { |ssl| assert_equal("TLSv1.2", ssl.ssl_version) } } end def test_forbid_tls_v1_1_for_client ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_1 } start_server_version(:SSLv23, ctx_proc) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.ssl_version = :TLSv1_1 assert_handshake_error { server_connect(port, ctx) } } end if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) def test_forbid_tls_v1_1_from_server start_server_version(:TLSv1_1) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_1 assert_handshake_error { server_connect(port, ctx) } } end if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) def test_forbid_tls_v1_2_for_client ctx_proc = Proc.new { |ctx| ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_2 } start_server_version(:SSLv23, ctx_proc) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.ssl_version = :TLSv1_2 assert_handshake_error { server_connect(port, ctx) } } end if defined?(OpenSSL::SSL::OP_NO_TLSv1_2) def test_forbid_tls_v1_2_from_server start_server_version(:TLSv1_2) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_TLSv1_2 assert_handshake_error { server_connect(port, ctx) } } end if defined?(OpenSSL::SSL::OP_NO_TLSv1_2) end def test_renegotiation_cb num_handshakes = 0 renegotiation_cb = Proc.new { |ssl| num_handshakes += 1 } ctx_proc = Proc.new { |ctx| ctx.renegotiation_cb = renegotiation_cb } start_server_version(:SSLv23, ctx_proc) { |port| server_connect(port) { |ssl| assert_equal(1, num_handshakes) ssl.puts "abc"; assert_equal "abc\n", ssl.gets } } end if openssl?(1, 0, 2) || libressl? def test_alpn_protocol_selection_ary advertised = ["http/1.1", "spdy/2"] ctx_proc = Proc.new { |ctx| ctx.alpn_select_cb = -> (protocols) { protocols.first } ctx.alpn_protocols = advertised } start_server_version(:SSLv23, ctx_proc) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.alpn_protocols = advertised server_connect(port, ctx) { |ssl| assert_equal(advertised.first, ssl.alpn_protocol) ssl.puts "abc"; assert_equal "abc\n", ssl.gets } } end def test_alpn_protocol_selection_cancel sock1, sock2 = socketpair ctx1 = OpenSSL::SSL::SSLContext.new ctx1.cert = @svr_cert ctx1.key = @svr_key ctx1.tmp_dh_callback = proc { Fixtures.pkey_dh("dh1024") } ctx1.alpn_select_cb = -> (protocols) { nil } ssl1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1) ctx2 = OpenSSL::SSL::SSLContext.new ctx2.alpn_protocols = ["http/1.1"] ssl2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) t = Thread.new { ssl2.connect_nonblock(exception: false) } assert_raise_with_message(TypeError, /nil/) { ssl1.accept } t.join ensure sock1&.close sock2&.close ssl1&.close ssl2&.close t&.kill t&.join end end def test_npn_protocol_selection_ary pend "TLS 1.2 is not supported" unless tls12_supported? pend "NPN is not supported" unless \ OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb) pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1) advertised = ["http/1.1", "spdy/2"] ctx_proc = proc { |ctx| ctx.npn_protocols = advertised } start_server_version(:TLSv1_2, ctx_proc) { |port| selector = lambda { |which| ctx = OpenSSL::SSL::SSLContext.new ctx.npn_select_cb = -> (protocols) { protocols.send(which) } server_connect(port, ctx) { |ssl| assert_equal(advertised.send(which), ssl.npn_protocol) } } selector.call(:first) selector.call(:last) } end def test_npn_protocol_selection_enum pend "TLS 1.2 is not supported" unless tls12_supported? pend "NPN is not supported" unless \ OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb) pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1) advertised = Object.new def advertised.each yield "http/1.1" yield "spdy/2" end ctx_proc = Proc.new { |ctx| ctx.npn_protocols = advertised } start_server_version(:TLSv1_2, ctx_proc) { |port| selector = lambda { |selected, which| ctx = OpenSSL::SSL::SSLContext.new ctx.npn_select_cb = -> (protocols) { protocols.to_a.send(which) } server_connect(port, ctx) { |ssl| assert_equal(selected, ssl.npn_protocol) } } selector.call("http/1.1", :first) selector.call("spdy/2", :last) } end def test_npn_protocol_selection_cancel pend "TLS 1.2 is not supported" unless tls12_supported? pend "NPN is not supported" unless \ OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb) pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1) ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] } start_server_version(:TLSv1_2, ctx_proc) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.npn_select_cb = -> (protocols) { raise RuntimeError.new } assert_raise(RuntimeError) { server_connect(port, ctx) } } end def test_npn_advertised_protocol_too_long pend "TLS 1.2 is not supported" unless tls12_supported? pend "NPN is not supported" unless \ OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb) pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1) ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["a" * 256] } start_server_version(:TLSv1_2, ctx_proc) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.npn_select_cb = -> (protocols) { protocols.first } assert_handshake_error { server_connect(port, ctx) } } end def test_npn_selected_protocol_too_long pend "TLS 1.2 is not supported" unless tls12_supported? pend "NPN is not supported" unless \ OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb) pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1) ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] } start_server_version(:TLSv1_2, ctx_proc) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.npn_select_cb = -> (protocols) { "a" * 256 } assert_handshake_error { server_connect(port, ctx) } } end def test_close_after_socket_close start_server { |port| sock = TCPSocket.new("127.0.0.1", port) ssl = OpenSSL::SSL::SSLSocket.new(sock) ssl.connect ssl.puts "abc"; assert_equal "abc\n", ssl.gets sock.close assert_nothing_raised do ssl.close end } end def test_sync_close_without_connect Socket.open(:INET, :STREAM) {|s| ssl = OpenSSL::SSL::SSLSocket.new(s) ssl.sync_close = true ssl.close assert(s.closed?) } end def test_get_ephemeral_key # OpenSSL >= 1.0.2 unless OpenSSL::SSL::SSLSocket.method_defined?(:tmp_key) pend "SSL_get_server_tmp_key() is not supported" end if tls12_supported? # kRSA ctx_proc1 = proc { |ctx| ctx.ssl_version = :TLSv1_2 ctx.ciphers = "kRSA" } start_server(ctx_proc: ctx_proc1) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.ssl_version = :TLSv1_2 ctx.ciphers = "kRSA" server_connect(port, ctx) { |ssl| assert_nil ssl.tmp_key } end end if defined?(OpenSSL::PKey::DH) && tls12_supported? # DHE # TODO: How to test this with TLS 1.3? ctx_proc2 = proc { |ctx| ctx.ssl_version = :TLSv1_2 ctx.ciphers = "EDH" } start_server(ctx_proc: ctx_proc2) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.ssl_version = :TLSv1_2 ctx.ciphers = "EDH" server_connect(port, ctx) { |ssl| assert_instance_of OpenSSL::PKey::DH, ssl.tmp_key } end end if defined?(OpenSSL::PKey::EC) # ECDHE ctx_proc3 = proc { |ctx| ctx.ciphers = "DEFAULT:!kRSA:!kEDH" ctx.ecdh_curves = "P-256" } start_server(ctx_proc: ctx_proc3) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.ciphers = "DEFAULT:!kRSA:!kEDH" server_connect(port, ctx) { |ssl| assert_instance_of OpenSSL::PKey::EC, ssl.tmp_key ssl.puts "abc"; assert_equal "abc\n", ssl.gets } end end end def test_dh_callback pend "TLS 1.2 is not supported" unless tls12_supported? called = false ctx_proc = -> ctx { ctx.ssl_version = :TLSv1_2 ctx.ciphers = "DH:!NULL" ctx.tmp_dh_callback = ->(*args) { called = true Fixtures.pkey_dh("dh1024") } } start_server(ctx_proc: ctx_proc) do |port| server_connect(port) { |ssl| assert called, "dh callback should be called" if ssl.respond_to?(:tmp_key) assert_equal Fixtures.pkey_dh("dh1024").to_der, ssl.tmp_key.to_der end } end end def test_connect_works_when_setting_dh_callback_to_nil pend "TLS 1.2 is not supported" unless tls12_supported? ctx_proc = -> ctx { ctx.ssl_version = :TLSv1_2 ctx.ciphers = "DH:!NULL" # use DH ctx.tmp_dh_callback = nil } start_server(ctx_proc: ctx_proc) do |port| EnvUtil.suppress_warning { # uses default callback assert_nothing_raised { server_connect(port) { } } } end end def test_tmp_ecdh_callback pend "EC is disabled" unless defined?(OpenSSL::PKey::EC) pend "tmp_ecdh_callback is not supported" unless \ OpenSSL::SSL::SSLContext.method_defined?(:tmp_ecdh_callback) pend "LibreSSL 2.6 has broken SSL_CTX_set_tmp_ecdh_callback()" \ if libressl?(2, 6, 1) EnvUtil.suppress_warning do # tmp_ecdh_callback is deprecated (2016-05) called = false ctx_proc = -> ctx { ctx.ciphers = "DEFAULT:!kRSA:!kEDH" ctx.tmp_ecdh_callback = -> (*args) { called = true OpenSSL::PKey::EC.new "prime256v1" } } start_server(ctx_proc: ctx_proc) do |port| server_connect(port) { |s| assert called, "tmp_ecdh_callback should be called" } end end end def test_ecdh_curves pend "EC is disabled" unless defined?(OpenSSL::PKey::EC) ctx_proc = -> ctx { # Enable both ECDHE (~ TLS 1.2) cipher suites and TLS 1.3 ctx.ciphers = "DEFAULT:!kRSA:!kEDH" ctx.ecdh_curves = "P-384:P-521" } start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port| ctx = OpenSSL::SSL::SSLContext.new ctx.ecdh_curves = "P-256:P-384" # disable P-521 for OpenSSL >= 1.0.2 server_connect(port, ctx) { |ssl| cs = ssl.cipher[0] if /\ATLS/ =~ cs # Is TLS 1.3 is used? assert_equal "secp384r1", ssl.tmp_key.group.curve_name else assert_match (/\AECDH/), cs if ssl.respond_to?(:tmp_key) assert_equal "secp384r1", ssl.tmp_key.group.curve_name end end ssl.puts "abc"; assert_equal "abc\n", ssl.gets } if openssl?(1, 0, 2) || libressl?(2, 5, 1) ctx = OpenSSL::SSL::SSLContext.new ctx.ecdh_curves = "P-256" assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx) { } } ctx = OpenSSL::SSL::SSLContext.new ctx.ecdh_curves = "P-521:P-384" server_connect(port, ctx) { |ssl| assert_equal "secp521r1", ssl.tmp_key.group.curve_name ssl.puts "abc"; assert_equal "abc\n", ssl.gets } end end end def test_security_level ctx = OpenSSL::SSL::SSLContext.new begin ctx.security_level = 1 rescue NotImplementedError assert_equal(0, ctx.security_level) return end assert_equal(1, ctx.security_level) # assert_raise(OpenSSL::SSL::SSLError) { ctx.key = Fixtures.pkey("dsa512") } # ctx.key = Fixtures.pkey("rsa1024") # ctx.security_level = 2 # assert_raise(OpenSSL::SSL::SSLError) { ctx.key = Fixtures.pkey("rsa1024") } pend "FIXME: SSLContext#key= currently does not raise because SSL_CTX_use_certificate() is delayed" end def test_dup ctx = OpenSSL::SSL::SSLContext.new sock1, sock2 = socketpair ssl = OpenSSL::SSL::SSLSocket.new(sock1, ctx) assert_raise(NoMethodError) { ctx.dup } assert_raise(NoMethodError) { ssl.dup } ensure ssl.close if ssl sock1.close sock2.close end def test_freeze_calls_setup bug = "[ruby/openssl#85]" start_server(ignore_listener_error: true) { |port| ctx = OpenSSL::SSL::SSLContext.new ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER ctx.freeze assert_raise(OpenSSL::SSL::SSLError, bug) { server_connect(port, ctx) } } end private def start_server_version(version, ctx_proc = nil, server_proc = method(:readwrite_loop), &blk) ctx_wrap = Proc.new { |ctx| ctx.ssl_version = version ctx_proc.call(ctx) if ctx_proc } start_server( ctx_proc: ctx_wrap, server_proc: server_proc, ignore_listener_error: true, &blk ) end def server_connect(port, ctx = nil) sock = TCPSocket.new("127.0.0.1", port) ssl = ctx ? OpenSSL::SSL::SSLSocket.new(sock, ctx) : OpenSSL::SSL::SSLSocket.new(sock) ssl.sync_close = true ssl.connect yield ssl if block_given? ensure if ssl ssl.close elsif sock sock.close end end def assert_handshake_error # different OpenSSL versions react differently when facing a SSL/TLS version # that has been marked as forbidden, therefore either of these may be raised assert_raise(OpenSSL::SSL::SSLError, Errno::ECONNRESET) { yield } end end end openssl-2.0.9/test/test_ssl_session.rb000066400000000000000000000323051336157045000201230ustar00rootroot00000000000000# frozen_string_literal: false require_relative "utils" if defined?(OpenSSL::TestUtils) class OpenSSL::TestSSLSession < OpenSSL::SSLTestCase def test_session pend "TLS 1.2 is not supported" unless tls12_supported? ctx_proc = proc { |ctx| ctx.ssl_version = :TLSv1_2 } start_server(ctx_proc: ctx_proc) do |port| server_connect_with_session(port, nil, nil) { |ssl| session = ssl.session assert(session == OpenSSL::SSL::Session.new(session.to_pem)) assert(session == OpenSSL::SSL::Session.new(ssl)) session.timeout = 5 assert_equal(5, session.timeout) assert_not_nil(session.time) # SSL_SESSION_time keeps long value so we can't keep nsec fragment. session.time = t1 = Time.now.to_i assert_equal(Time.at(t1), session.time) assert_not_nil(session.id) pem = session.to_pem assert_match(/\A-----BEGIN SSL SESSION PARAMETERS-----/, pem) assert_match(/-----END SSL SESSION PARAMETERS-----\Z/, pem) pem.gsub!(/-----(BEGIN|END) SSL SESSION PARAMETERS-----/, '').gsub!(/[\r\n]+/m, '') assert_equal(session.to_der, pem.unpack('m*')[0]) assert_not_nil(session.to_text) } end end DUMMY_SESSION = <<__EOS__ -----BEGIN SSL SESSION PARAMETERS----- MIIDzQIBAQICAwEEAgA5BCAF219w9ZEV8dNA60cpEGOI34hJtIFbf3bkfzSgMyad MQQwyGLbkCxE4OiMLdKKem+pyh8V7ifoP7tCxhdmwoDlJxI1v6nVCjai+FGYuncy NNSWoQYCBE4DDWuiAwIBCqOCAo4wggKKMIIBcqADAgECAgECMA0GCSqGSIb3DQEB BQUAMD0xEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5 LWxhbmcxCzAJBgNVBAMMAkNBMB4XDTExMDYyMzA5NTQ1MVoXDTExMDYyMzEwMjQ1 MVowRDETMBEGCgmSJomT8ixkARkWA29yZzEZMBcGCgmSJomT8ixkARkWCXJ1Ynkt bGFuZzESMBAGA1UEAwwJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB iQKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7CxaKPERYHs k4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/Q3geLv8Z D9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQABoxIwEDAO BgNVHQ8BAf8EBAMCBaAwDQYJKoZIhvcNAQEFBQADggEBACj5WhoZ/ODVeHpwgq1d 8fW/13ICRYHYpv6dzlWihyqclGxbKMlMnaVCPz+4JaVtMz3QB748KJQgL3Llg3R1 ek+f+n1MBCMfFFsQXJ2gtLB84zD6UCz8aaCWN5/czJCd7xMz7fRLy3TOIW5boXAU zIa8EODk+477K1uznHm286ab0Clv+9d304hwmBZgkzLg6+31Of6d6s0E0rwLGiS2 sOWYg34Y3r4j8BS9Ak4jzpoLY6cJ0QAKCOJCgmjGr4XHpyXMLbicp3ga1uSbwtVO gF/gTfpLhJC+y0EQ5x3Ftl88Cq7ZJuLBDMo/TLIfReJMQu/HlrTT7+LwtneSWGmr KkSkAgQApQMCAROqgcMEgcAuDkAVfj6QAJMz9yqTzW5wPFyty7CxUEcwKjUqj5UP /Yvky1EkRuM/eQfN7ucY+MUvMqv+R8ZSkHPsnjkBN5ChvZXjrUSZKFVjR4eFVz2V jismLEJvIFhQh6pqTroRrOjMfTaM5Lwoytr2FTGobN9rnjIRsXeFQW1HLFbXn7Dh 8uaQkMwIVVSGRB8T7t6z6WIdWruOjCZ6G5ASI5XoqAHwGezhLodZuvJEfsVyCF9y j+RBGfCFrrQbBdnkFI/ztgM= -----END SSL SESSION PARAMETERS----- __EOS__ DUMMY_SESSION_NO_EXT = <<-__EOS__ -----BEGIN SSL SESSION PARAMETERS----- MIIDCAIBAQICAwAEAgA5BCDyAW7rcpzMjDSosH+Tv6sukymeqgq3xQVVMez628A+ lAQw9TrKzrIqlHEh6ltuQaqv/Aq83AmaAlogYktZgXAjOGnhX7ifJDNLMuCfQq53 hPAaoQYCBE4iDeeiBAICASyjggKOMIICijCCAXKgAwIBAgIBAjANBgkqhkiG9w0B AQUFADA9MRMwEQYKCZImiZPyLGQBGRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVi eS1sYW5nMQswCQYDVQQDDAJDQTAeFw0xMTA3MTYyMjE3MTFaFw0xMTA3MTYyMjQ3 MTFaMEQxEzARBgoJkiaJk/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5 LWxhbmcxEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw gYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8Atv5y/OVQRp0ag8Tqo1YewsWijxEWB 7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9JWsjah8ssEBFSxZqdXKSLf0N4Hi7/ GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/peeBOpRfyXxRFOYcCAwEAAaMSMBAw DgYDVR0PAQH/BAQDAgWgMA0GCSqGSIb3DQEBBQUAA4IBAQA3TRzABRG3kz8jEEYr tDQqXgsxwTsLhTT5d1yF0D8uFw+y15hJAJnh6GJHjqhWBrF4zNoTApFo+4iIL6g3 q9C3mUsxIVAHx41DwZBh/FI7J4FqlAoGOguu7892CNVY3ZZjc3AXMTdKjcNoWPzz FCdj5fNT24JMMe+ZdGZK97ChahJsdn/6B3j6ze9NK9mfYEbiJhejGTPLOFVHJCGR KYYZ3ZcKhLDr9ql4d7cCo1gBtemrmFQGPui7GttNEqmXqUKvV8mYoa8farf5i7T4 L6a/gp2cVZTaDIS1HjbJsA/Ag7AajZqiN6LfqShNUVsrMZ+5CoV8EkBDTZPJ9MSr a3EqpAIEAKUDAgET -----END SSL SESSION PARAMETERS----- __EOS__ def test_session_time sess = OpenSSL::SSL::Session.new(DUMMY_SESSION_NO_EXT) sess.time = (now = Time.now) assert_equal(now.to_i, sess.time.to_i) sess.time = 1 assert_equal(1, sess.time.to_i) sess.time = 1.2345 assert_equal(1, sess.time.to_i) # Can OpenSSL handle t>2038y correctly? Version? sess.time = 2**31 - 1 assert_equal(2**31 - 1, sess.time.to_i) end def test_session_timeout sess = OpenSSL::SSL::Session.new(DUMMY_SESSION_NO_EXT) assert_raise(TypeError) do sess.timeout = Time.now end sess.timeout = 1 assert_equal(1, sess.timeout.to_i) sess.timeout = 1.2345 assert_equal(1, sess.timeout.to_i) sess.timeout = 2**31 - 1 assert_equal(2**31 - 1, sess.timeout.to_i) end def test_session_exts_read assert(OpenSSL::SSL::Session.new(DUMMY_SESSION)) end def test_resumption non_resumable = nil start_server { |port| server_connect_with_session(port, nil, nil) { |ssl| ssl.puts "abc"; assert_equal "abc\n", ssl.gets non_resumable = ssl.session } } ctx_proc = proc { |ctx| ctx.options &= ~OpenSSL::SSL::OP_NO_TICKET # Disable server-side session cache which is enabled by default ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_OFF } start_server(ctx_proc: ctx_proc) do |port| sess1 = server_connect_with_session(port, nil, nil) { |ssl| ssl.puts("abc"); assert_equal "abc\n", ssl.gets assert_equal false, ssl.session_reused? ssl.session } server_connect_with_session(port, nil, non_resumable) { |ssl| ssl.puts("abc"); assert_equal "abc\n", ssl.gets assert_equal false, ssl.session_reused? } server_connect_with_session(port, nil, sess1) { |ssl| ssl.puts("abc"); assert_equal "abc\n", ssl.gets assert_equal true, ssl.session_reused? } end end def test_server_session_cache pend "TLS 1.2 is not supported" unless tls12_supported? ctx_proc = Proc.new do |ctx| ctx.ssl_version = :TLSv1_2 ctx.options |= OpenSSL::SSL::OP_NO_TICKET end connections = nil saved_session = nil server_proc = Proc.new do |ctx, ssl| stats = ctx.session_cache_stats case connections when 0 assert_equal false, ssl.session_reused? assert_equal 1, stats[:cache_num] assert_equal 0, stats[:cache_hits] assert_equal 0, stats[:cache_misses] when 1 assert_equal true, ssl.session_reused? assert_equal 1, stats[:cache_num] assert_equal 1, stats[:cache_hits] assert_equal 0, stats[:cache_misses] saved_session = ssl.session assert_equal true, ctx.session_remove(ssl.session) when 2 assert_equal false, ssl.session_reused? assert_equal 1, stats[:cache_num] assert_equal 1, stats[:cache_hits] assert_equal 1, stats[:cache_misses] assert_equal true, ctx.session_add(saved_session.dup) when 3 assert_equal true, ssl.session_reused? assert_equal 2, stats[:cache_num] assert_equal 2, stats[:cache_hits] assert_equal 1, stats[:cache_misses] ctx.flush_sessions(Time.now + 10000) when 4 assert_equal false, ssl.session_reused? assert_equal 1, stats[:cache_num] assert_equal 2, stats[:cache_hits] assert_equal 2, stats[:cache_misses] assert_equal true, ctx.session_add(saved_session.dup) end readwrite_loop(ctx, ssl) end start_server(ctx_proc: ctx_proc, server_proc: server_proc) do |port| first_session = nil 10.times do |i| connections = i cctx = OpenSSL::SSL::SSLContext.new cctx.ssl_version = :TLSv1_2 server_connect_with_session(port, cctx, first_session) { |ssl| ssl.puts("abc"); assert_equal "abc\n", ssl.gets first_session ||= ssl.session case connections when 0; when 1; assert_equal true, ssl.session_reused? when 2; assert_equal false, ssl.session_reused? when 3; assert_equal true, ssl.session_reused? when 4; assert_equal false, ssl.session_reused? when 5..9; assert_equal true, ssl.session_reused? end } end end end # Skipping tests that use session_remove_cb by default because it may cause # deadlock. TEST_SESSION_REMOVE_CB = ENV["OSSL_TEST_ALL"] == "1" def test_ctx_client_session_cb pend "TLS 1.2 is not supported" unless tls12_supported? ctx_proc = proc { |ctx| ctx.ssl_version = :TLSv1_2 } start_server(ctx_proc: ctx_proc) do |port| called = {} ctx = OpenSSL::SSL::SSLContext.new ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT ctx.session_new_cb = lambda { |ary| sock, sess = ary called[:new] = [sock, sess] } if TEST_SESSION_REMOVE_CB ctx.session_remove_cb = lambda { |ary| ctx, sess = ary called[:remove] = [ctx, sess] # any resulting value is OK (ignored) } end server_connect_with_session(port, ctx, nil) { |ssl| assert_equal(1, ctx.session_cache_stats[:cache_num]) assert_equal(1, ctx.session_cache_stats[:connect_good]) assert_equal([ssl, ssl.session], called[:new]) assert(ctx.session_remove(ssl.session)) assert(!ctx.session_remove(ssl.session)) if TEST_SESSION_REMOVE_CB assert_equal([ctx, ssl.session], called[:remove]) end } end end def test_ctx_server_session_cb pend "TLS 1.2 is not supported" unless tls12_supported? connections = nil called = {} cctx = OpenSSL::SSL::SSLContext.new cctx.ssl_version = :TLSv1_2 sctx = nil ctx_proc = Proc.new { |ctx| sctx = ctx ctx.ssl_version = :TLSv1_2 ctx.options |= OpenSSL::SSL::OP_NO_TICKET # get_cb is called whenever a client proposed to resume a session but # the session could not be found in the internal session cache. last_server_session = nil ctx.session_get_cb = lambda { |ary| _sess, data = ary called[:get] = data if connections == 2 last_server_session.dup else nil end } ctx.session_new_cb = lambda { |ary| _sock, sess = ary called[:new] = sess last_server_session = sess } if TEST_SESSION_REMOVE_CB ctx.session_remove_cb = lambda { |ary| _ctx, sess = ary called[:remove] = sess } end } start_server(ctx_proc: ctx_proc) do |port| connections = 0 sess0 = server_connect_with_session(port, cctx, nil) { |ssl| ssl.puts("abc"); assert_equal "abc\n", ssl.gets assert_equal false, ssl.session_reused? ssl.session } assert_nil called[:get] assert_not_nil called[:new] assert_equal sess0.id, called[:new].id if TEST_SESSION_REMOVE_CB assert_nil called[:remove] end called.clear # Internal cache hit connections = 1 server_connect_with_session(port, cctx, sess0.dup) { |ssl| ssl.puts("abc"); assert_equal "abc\n", ssl.gets assert_equal true, ssl.session_reused? ssl.session } assert_nil called[:get] assert_nil called[:new] if TEST_SESSION_REMOVE_CB assert_nil called[:remove] end called.clear sctx.flush_sessions(Time.now + 10000) if TEST_SESSION_REMOVE_CB assert_not_nil called[:remove] assert_equal sess0.id, called[:remove].id end called.clear # External cache hit connections = 2 sess2 = server_connect_with_session(port, cctx, sess0.dup) { |ssl| ssl.puts("abc"); assert_equal "abc\n", ssl.gets if !ssl.session_reused? && openssl?(1, 1, 0) && !openssl?(1, 1, 0, 7) # OpenSSL >= 1.1.0, < 1.1.0g pend "External session cache is not working; " \ "see https://github.com/openssl/openssl/pull/4014" end assert_equal true, ssl.session_reused? ssl.session } assert_equal sess0.id, sess2.id assert_equal sess0.id, called[:get] assert_nil called[:new] if TEST_SESSION_REMOVE_CB assert_nil called[:remove] end called.clear sctx.flush_sessions(Time.now + 10000) if TEST_SESSION_REMOVE_CB assert_not_nil called[:remove] assert_equal sess0.id, called[:remove].id end called.clear # Cache miss connections = 3 sess3 = server_connect_with_session(port, cctx, sess0.dup) { |ssl| ssl.puts("abc"); assert_equal "abc\n", ssl.gets assert_equal false, ssl.session_reused? ssl.session } assert_not_equal sess0.id, sess3.id assert_equal sess0.id, called[:get] assert_not_nil called[:new] assert_equal sess3.id, called[:new].id if TEST_SESSION_REMOVE_CB assert_nil called[:remove] end end end def test_dup sess_orig = OpenSSL::SSL::Session.new(DUMMY_SESSION) sess_dup = sess_orig.dup assert_equal(sess_orig.to_der, sess_dup.to_der) end private def server_connect_with_session(port, ctx = nil, sess = nil) sock = TCPSocket.new("127.0.0.1", port) ctx ||= OpenSSL::SSL::SSLContext.new ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.session = sess if sess ssl.sync_close = true ssl.connect yield ssl if block_given? ensure ssl&.close sock&.close end end end openssl-2.0.9/test/test_x509attr.rb000066400000000000000000000040661336157045000171620ustar00rootroot00000000000000# frozen_string_literal: false require_relative "utils" if defined?(OpenSSL::TestUtils) class OpenSSL::TestX509Attribute < OpenSSL::TestCase def test_new ef = OpenSSL::X509::ExtensionFactory.new val = OpenSSL::ASN1::Set.new([OpenSSL::ASN1::Sequence.new([ ef.create_extension("keyUsage", "keyCertSign", true) ])]) attr = OpenSSL::X509::Attribute.new("extReq", val) assert_equal("extReq", attr.oid) assert_equal(val.to_der, attr.value.to_der) attr = OpenSSL::X509::Attribute.new("1.2.840.113549.1.9.14", val) assert_equal("extReq", attr.oid) end def test_from_der # oid: challengePassword, values: Set[UTF8String<"abc123">] test_der = "\x30\x15\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x07\x31\x08" \ "\x0c\x06\x61\x62\x63\x31\x32\x33".b attr = OpenSSL::X509::Attribute.new(test_der) assert_equal(test_der, attr.to_der) assert_equal("challengePassword", attr.oid) assert_equal("abc123", attr.value.value[0].value) end def test_to_der ef = OpenSSL::X509::ExtensionFactory.new val = OpenSSL::ASN1::Set.new([OpenSSL::ASN1::Sequence.new([ ef.create_extension("keyUsage", "keyCertSign", true) ])]) attr = OpenSSL::X509::Attribute.new("extReq", val) expected = OpenSSL::ASN1::Sequence.new([ OpenSSL::ASN1::ObjectId.new("extReq"), val ]) assert_equal(expected.to_der, attr.to_der) end def test_invalid_value # should not change the original value test_der = "\x30\x15\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x07\x31\x08" \ "\x0c\x06\x61\x62\x63\x31\x32\x33".b attr = OpenSSL::X509::Attribute.new(test_der) assert_raise(TypeError) { attr.value = "1234" } assert_equal(test_der, attr.to_der) assert_raise(OpenSSL::X509::AttributeError) { attr.oid = "abc123" } assert_equal(test_der, attr.to_der) end def test_dup val = OpenSSL::ASN1::Set([ OpenSSL::ASN1::UTF8String("abc123") ]) attr = OpenSSL::X509::Attribute.new("challengePassword", val) assert_equal(attr.to_der, attr.dup.to_der) end end end openssl-2.0.9/test/test_x509cert.rb000066400000000000000000000154741336157045000171520ustar00rootroot00000000000000# frozen_string_literal: false require_relative "utils" if defined?(OpenSSL::TestUtils) class OpenSSL::TestX509Certificate < OpenSSL::TestCase def setup super @rsa1024 = Fixtures.pkey("rsa1024") @rsa2048 = Fixtures.pkey("rsa2048") @dsa256 = Fixtures.pkey("dsa256") @dsa512 = Fixtures.pkey("dsa512") @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA") @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1") end def test_serial [1, 2**32, 2**100].each{|s| cert = issue_cert(@ca, @rsa2048, s, [], nil, nil) assert_equal(s, cert.serial) cert = OpenSSL::X509::Certificate.new(cert.to_der) assert_equal(s, cert.serial) } end def test_public_key exts = [ ["basicConstraints","CA:TRUE",true], ["subjectKeyIdentifier","hash",false], ["authorityKeyIdentifier","keyid:always",false], ] sha1 = OpenSSL::Digest::SHA1.new dsa_digest = OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new [ [@rsa1024, sha1], [@rsa2048, sha1], [@dsa256, dsa_digest], [@dsa512, dsa_digest] ].each{|pk, digest| cert = issue_cert(@ca, pk, 1, exts, nil, nil, digest: digest) assert_equal(cert.extensions.sort_by(&:to_s)[2].value, OpenSSL::TestUtils.get_subject_key_id(cert)) cert = OpenSSL::X509::Certificate.new(cert.to_der) assert_equal(cert.extensions.sort_by(&:to_s)[2].value, OpenSSL::TestUtils.get_subject_key_id(cert)) } end def test_validity now = Time.at(Time.now.to_i + 0.9) cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, not_before: now, not_after: now+3600) assert_equal(Time.at(now.to_i), cert.not_before) assert_equal(Time.at(now.to_i+3600), cert.not_after) now = Time.at(now.to_i) cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, not_before: now, not_after: now+3600) assert_equal(now.getutc, cert.not_before) assert_equal((now+3600).getutc, cert.not_after) now = Time.at(0) cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, not_before: now, not_after: now) assert_equal(now.getutc, cert.not_before) assert_equal(now.getutc, cert.not_after) now = Time.at(0x7fffffff) cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, not_before: now, not_after: now) assert_equal(now.getutc, cert.not_before) assert_equal(now.getutc, cert.not_after) end def test_extension ca_exts = [ ["basicConstraints","CA:TRUE",true], ["keyUsage","keyCertSign, cRLSign",true], ["subjectKeyIdentifier","hash",false], ["authorityKeyIdentifier","keyid:always",false], ] ca_cert = issue_cert(@ca, @rsa2048, 1, ca_exts, nil, nil) ca_cert.extensions.each_with_index{|ext, i| assert_equal(ca_exts[i].first, ext.oid) assert_equal(ca_exts[i].last, ext.critical?) } ee1_exts = [ ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true], ["subjectKeyIdentifier","hash",false], ["authorityKeyIdentifier","keyid:always",false], ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false], ["subjectAltName","email:ee1@ruby-lang.org",false], ] ee1_cert = issue_cert(@ee1, @rsa1024, 2, ee1_exts, ca_cert, @rsa2048) assert_equal(ca_cert.subject.to_der, ee1_cert.issuer.to_der) ee1_cert.extensions.each_with_index{|ext, i| assert_equal(ee1_exts[i].first, ext.oid) assert_equal(ee1_exts[i].last, ext.critical?) } end def test_sign_and_verify_rsa_sha1 cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, digest: "sha1") assert_equal(false, cert.verify(@rsa1024)) assert_equal(true, cert.verify(@rsa2048)) assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) }) assert_equal(false, certificate_error_returns_false { cert.verify(@dsa512) }) cert.serial = 2 assert_equal(false, cert.verify(@rsa2048)) end def test_sign_and_verify_rsa_md5 cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, digest: "md5") assert_equal(false, cert.verify(@rsa1024)) assert_equal(true, cert.verify(@rsa2048)) assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) }) assert_equal(false, certificate_error_returns_false { cert.verify(@dsa512) }) cert.subject = @ee1 assert_equal(false, cert.verify(@rsa2048)) rescue OpenSSL::X509::CertificateError # RHEL7 disables MD5 end def test_sign_and_verify_dsa cert = issue_cert(@ca, @dsa512, 1, [], nil, nil) assert_equal(false, certificate_error_returns_false { cert.verify(@rsa1024) }) assert_equal(false, certificate_error_returns_false { cert.verify(@rsa2048) }) assert_equal(false, cert.verify(@dsa256)) assert_equal(true, cert.verify(@dsa512)) cert.not_after = Time.now assert_equal(false, cert.verify(@dsa512)) end def test_sign_and_verify_rsa_dss1 cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, digest: OpenSSL::Digest::DSS1.new) assert_equal(false, cert.verify(@rsa1024)) assert_equal(true, cert.verify(@rsa2048)) assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) }) assert_equal(false, certificate_error_returns_false { cert.verify(@dsa512) }) cert.subject = @ee1 assert_equal(false, cert.verify(@rsa2048)) rescue OpenSSL::X509::CertificateError end if defined?(OpenSSL::Digest::DSS1) def test_sign_and_verify_dsa_md5 assert_raise(OpenSSL::X509::CertificateError){ issue_cert(@ca, @dsa512, 1, [], nil, nil, digest: "md5") } end def test_dsig_algorithm_mismatch assert_raise(OpenSSL::X509::CertificateError) do issue_cert(@ca, @rsa2048, 1, [], nil, nil, digest: OpenSSL::Digest::DSS1.new) end if OpenSSL::OPENSSL_VERSION_NUMBER < 0x10001000 # [ruby-core:42949] end def test_dsa_with_sha2 begin cert = issue_cert(@ca, @dsa256, 1, [], nil, nil, digest: "sha256") assert_equal("dsa_with_SHA256", cert.signature_algorithm) rescue OpenSSL::X509::CertificateError # dsa_with_sha2 not supported. skip following test. return end # TODO: need more tests for dsa + sha2 # SHA1 is allowed from OpenSSL 1.0.0 (0.9.8 requires DSS1) cert = issue_cert(@ca, @dsa256, 1, [], nil, nil, digest: "sha1") assert_equal("dsaWithSHA1", cert.signature_algorithm) end if defined?(OpenSSL::Digest::SHA256) def test_check_private_key cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil) assert_equal(true, cert.check_private_key(@rsa2048)) end def test_read_from_file cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil) Tempfile.create("cert") { |f| f << cert.to_pem f.rewind assert_equal cert.to_der, OpenSSL::X509::Certificate.new(f).to_der } end private def certificate_error_returns_false yield rescue OpenSSL::X509::CertificateError false end end end openssl-2.0.9/test/test_x509crl.rb000066400000000000000000000173251336157045000167720ustar00rootroot00000000000000# frozen_string_literal: false require_relative "utils" if defined?(OpenSSL::TestUtils) class OpenSSL::TestX509CRL < OpenSSL::TestCase def setup super @rsa1024 = Fixtures.pkey("rsa1024") @rsa2048 = Fixtures.pkey("rsa2048") @dsa256 = Fixtures.pkey("dsa256") @dsa512 = Fixtures.pkey("dsa512") @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA") @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1") @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2") end def test_basic now = Time.at(Time.now.to_i) cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil) crl = issue_crl([], 1, now, now+1600, [], cert, @rsa2048, OpenSSL::Digest::SHA1.new) assert_equal(1, crl.version) assert_equal(cert.issuer.to_der, crl.issuer.to_der) assert_equal(now, crl.last_update) assert_equal(now+1600, crl.next_update) crl = OpenSSL::X509::CRL.new(crl.to_der) assert_equal(1, crl.version) assert_equal(cert.issuer.to_der, crl.issuer.to_der) assert_equal(now, crl.last_update) assert_equal(now+1600, crl.next_update) end def test_revoked # CRLReason ::= ENUMERATED { # unspecified (0), # keyCompromise (1), # cACompromise (2), # affiliationChanged (3), # superseded (4), # cessationOfOperation (5), # certificateHold (6), # removeFromCRL (8), # privilegeWithdrawn (9), # aACompromise (10) } now = Time.at(Time.now.to_i) revoke_info = [ [1, Time.at(0), 1], [2, Time.at(0x7fffffff), 2], [3, now, 3], [4, now, 4], [5, now, 5], ] cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil) crl = issue_crl(revoke_info, 1, Time.now, Time.now+1600, [], cert, @rsa2048, OpenSSL::Digest::SHA1.new) revoked = crl.revoked assert_equal(5, revoked.size) assert_equal(1, revoked[0].serial) assert_equal(2, revoked[1].serial) assert_equal(3, revoked[2].serial) assert_equal(4, revoked[3].serial) assert_equal(5, revoked[4].serial) assert_equal(Time.at(0), revoked[0].time) assert_equal(Time.at(0x7fffffff), revoked[1].time) assert_equal(now, revoked[2].time) assert_equal(now, revoked[3].time) assert_equal(now, revoked[4].time) assert_equal("CRLReason", revoked[0].extensions[0].oid) assert_equal("CRLReason", revoked[1].extensions[0].oid) assert_equal("CRLReason", revoked[2].extensions[0].oid) assert_equal("CRLReason", revoked[3].extensions[0].oid) assert_equal("CRLReason", revoked[4].extensions[0].oid) assert_equal("Key Compromise", revoked[0].extensions[0].value) assert_equal("CA Compromise", revoked[1].extensions[0].value) assert_equal("Affiliation Changed", revoked[2].extensions[0].value) assert_equal("Superseded", revoked[3].extensions[0].value) assert_equal("Cessation Of Operation", revoked[4].extensions[0].value) assert_equal(false, revoked[0].extensions[0].critical?) assert_equal(false, revoked[1].extensions[0].critical?) assert_equal(false, revoked[2].extensions[0].critical?) assert_equal(false, revoked[3].extensions[0].critical?) assert_equal(false, revoked[4].extensions[0].critical?) assert_equal("Key Compromise", revoked[0].extensions[0].value) assert_equal("CA Compromise", revoked[1].extensions[0].value) assert_equal("Affiliation Changed", revoked[2].extensions[0].value) assert_equal("Superseded", revoked[3].extensions[0].value) assert_equal("Cessation Of Operation", revoked[4].extensions[0].value) revoke_info = (1..1000).collect{|i| [i, now, 0] } crl = issue_crl(revoke_info, 1, Time.now, Time.now+1600, [], cert, @rsa2048, OpenSSL::Digest::SHA1.new) revoked = crl.revoked assert_equal(1000, revoked.size) assert_equal(1, revoked[0].serial) assert_equal(1000, revoked[999].serial) crl.revoked = revoked revoked2 = crl.revoked assert_equal(revoked.map(&:serial), revoked2.map(&:serial)) end def test_extension cert_exts = [ ["basicConstraints", "CA:TRUE", true], ["subjectKeyIdentifier", "hash", false], ["authorityKeyIdentifier", "keyid:always", false], ["subjectAltName", "email:xyzzy@ruby-lang.org", false], ["keyUsage", "cRLSign, keyCertSign", true], ] crl_exts = [ ["authorityKeyIdentifier", "keyid:always", false], ["issuerAltName", "issuer:copy", false], ] cert = issue_cert(@ca, @rsa2048, 1, cert_exts, nil, nil) crl = issue_crl([], 1, Time.now, Time.now+1600, crl_exts, cert, @rsa2048, OpenSSL::Digest::SHA1.new) exts = crl.extensions assert_equal(3, exts.size) assert_equal("1", exts[0].value) assert_equal("crlNumber", exts[0].oid) assert_equal(false, exts[0].critical?) assert_equal("authorityKeyIdentifier", exts[1].oid) keyid = OpenSSL::TestUtils.get_subject_key_id(cert) assert_match(/^keyid:#{keyid}/, exts[1].value) assert_equal(false, exts[1].critical?) assert_equal("issuerAltName", exts[2].oid) assert_equal("email:xyzzy@ruby-lang.org", exts[2].value) assert_equal(false, exts[2].critical?) crl = OpenSSL::X509::CRL.new(crl.to_der) exts = crl.extensions assert_equal(3, exts.size) assert_equal("1", exts[0].value) assert_equal("crlNumber", exts[0].oid) assert_equal(false, exts[0].critical?) assert_equal("authorityKeyIdentifier", exts[1].oid) keyid = OpenSSL::TestUtils.get_subject_key_id(cert) assert_match(/^keyid:#{keyid}/, exts[1].value) assert_equal(false, exts[1].critical?) assert_equal("issuerAltName", exts[2].oid) assert_equal("email:xyzzy@ruby-lang.org", exts[2].value) assert_equal(false, exts[2].critical?) end def test_crlnumber cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil) crl = issue_crl([], 1, Time.now, Time.now+1600, [], cert, @rsa2048, OpenSSL::Digest::SHA1.new) assert_match(1.to_s, crl.extensions[0].value) assert_match(/X509v3 CRL Number:\s+#{1}/m, crl.to_text) crl = issue_crl([], 2**32, Time.now, Time.now+1600, [], cert, @rsa2048, OpenSSL::Digest::SHA1.new) assert_match((2**32).to_s, crl.extensions[0].value) assert_match(/X509v3 CRL Number:\s+#{2**32}/m, crl.to_text) crl = issue_crl([], 2**100, Time.now, Time.now+1600, [], cert, @rsa2048, OpenSSL::Digest::SHA1.new) assert_match(/X509v3 CRL Number:\s+#{2**100}/m, crl.to_text) assert_match((2**100).to_s, crl.extensions[0].value) end def test_sign_and_verify cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil) crl = issue_crl([], 1, Time.now, Time.now+1600, [], cert, @rsa2048, OpenSSL::Digest::SHA1.new) assert_equal(false, crl.verify(@rsa1024)) assert_equal(true, crl.verify(@rsa2048)) assert_equal(false, crl_error_returns_false { crl.verify(@dsa256) }) assert_equal(false, crl_error_returns_false { crl.verify(@dsa512) }) crl.version = 0 assert_equal(false, crl.verify(@rsa2048)) cert = issue_cert(@ca, @dsa512, 1, [], nil, nil) crl = issue_crl([], 1, Time.now, Time.now+1600, [], cert, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new) assert_equal(false, crl_error_returns_false { crl.verify(@rsa1024) }) assert_equal(false, crl_error_returns_false { crl.verify(@rsa2048) }) assert_equal(false, crl.verify(@dsa256)) assert_equal(true, crl.verify(@dsa512)) crl.version = 0 assert_equal(false, crl.verify(@dsa512)) end private def crl_error_returns_false yield rescue OpenSSL::X509::CRLError false end end end openssl-2.0.9/test/test_x509ext.rb000066400000000000000000000052641336157045000170110ustar00rootroot00000000000000# frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestX509Extension < OpenSSL::TestCase def setup super @basic_constraints_value = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::Boolean(true), # CA OpenSSL::ASN1::Integer(2) # pathlen ]) @basic_constraints = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::ObjectId("basicConstraints"), OpenSSL::ASN1::Boolean(true), OpenSSL::ASN1::OctetString(@basic_constraints_value.to_der), ]) end def test_new ext = OpenSSL::X509::Extension.new(@basic_constraints.to_der) assert_equal("basicConstraints", ext.oid) assert_equal(true, ext.critical?) assert_equal("CA:TRUE, pathlen:2", ext.value) ext = OpenSSL::X509::Extension.new("2.5.29.19", @basic_constraints_value.to_der, true) assert_equal(@basic_constraints.to_der, ext.to_der) end def test_create_by_factory ef = OpenSSL::X509::ExtensionFactory.new bc = ef.create_extension("basicConstraints", "critical, CA:TRUE, pathlen:2") assert_equal(@basic_constraints.to_der, bc.to_der) bc = ef.create_extension("basicConstraints", "CA:TRUE, pathlen:2", true) assert_equal(@basic_constraints.to_der, bc.to_der) ef.config = OpenSSL::Config.parse(<<-_end_of_cnf_) [crlDistPts] URI.1 = http://www.example.com/crl URI.2 = ldap://ldap.example.com/cn=ca?certificateRevocationList;binary [certPolicies] policyIdentifier = 2.23.140.1.2.1 CPS.1 = http://cps.example.com _end_of_cnf_ cdp = ef.create_extension("crlDistributionPoints", "@crlDistPts") assert_equal(false, cdp.critical?) assert_equal("crlDistributionPoints", cdp.oid) assert_match(%{URI:http://www\.example\.com/crl}, cdp.value) assert_match( %r{URI:ldap://ldap\.example\.com/cn=ca\?certificateRevocationList;binary}, cdp.value) cdp = ef.create_extension("crlDistributionPoints", "critical, @crlDistPts") assert_equal(true, cdp.critical?) assert_equal("crlDistributionPoints", cdp.oid) assert_match(%{URI:http://www.example.com/crl}, cdp.value) assert_match( %r{URI:ldap://ldap.example.com/cn=ca\?certificateRevocationList;binary}, cdp.value) cp = ef.create_extension("certificatePolicies", "@certPolicies") assert_equal(false, cp.critical?) assert_equal("certificatePolicies", cp.oid) assert_match(%r{2.23.140.1.2.1}, cp.value) assert_match(%r{http://cps.example.com}, cp.value) end def test_dup ext = OpenSSL::X509::Extension.new(@basic_constraints.to_der) assert_equal(@basic_constraints.to_der, ext.to_der) assert_equal(ext.to_der, ext.dup.to_der) end end end openssl-2.0.9/test/test_x509name.rb000066400000000000000000000353311336157045000171270ustar00rootroot00000000000000# coding: ASCII-8BIT # frozen_string_literal: false require_relative 'utils' if defined?(OpenSSL::TestUtils) class OpenSSL::TestX509Name < OpenSSL::TestCase def setup super @obj_type_tmpl = Hash.new(OpenSSL::ASN1::PRINTABLESTRING) @obj_type_tmpl.update(OpenSSL::X509::Name::OBJECT_TYPE_TEMPLATE) end def test_s_new dn = [ ["C", "JP"], ["O", "example"], ["CN", "www.example.jp"] ] name = OpenSSL::X509::Name.new(dn) ary = name.to_a assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s) assert_equal("C", ary[0][0]) assert_equal("O", ary[1][0]) assert_equal("CN", ary[2][0]) assert_equal("JP", ary[0][1]) assert_equal("example", ary[1][1]) assert_equal("www.example.jp", ary[2][1]) assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2]) assert_equal(OpenSSL::ASN1::UTF8STRING, ary[1][2]) assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2]) dn = [ ["countryName", "JP"], ["organizationName", "example"], ["commonName", "www.example.jp"] ] name = OpenSSL::X509::Name.new(dn) ary = name.to_a assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s) assert_equal("C", ary[0][0]) assert_equal("O", ary[1][0]) assert_equal("CN", ary[2][0]) assert_equal("JP", ary[0][1]) assert_equal("example", ary[1][1]) assert_equal("www.example.jp", ary[2][1]) assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2]) assert_equal(OpenSSL::ASN1::UTF8STRING, ary[1][2]) assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2]) name = OpenSSL::X509::Name.new(dn, @obj_type_tmpl) ary = name.to_a assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s) assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2]) assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[1][2]) assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2]) dn = [ ["countryName", "JP", OpenSSL::ASN1::PRINTABLESTRING], ["organizationName", "example", OpenSSL::ASN1::PRINTABLESTRING], ["commonName", "www.example.jp", OpenSSL::ASN1::PRINTABLESTRING] ] name = OpenSSL::X509::Name.new(dn) ary = name.to_a assert_equal("/C=JP/O=example/CN=www.example.jp", name.to_s) assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[0][2]) assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[1][2]) assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2]) dn = [ ["DC", "org"], ["DC", "ruby-lang"], ["CN", "GOTOU Yuuzou"], ["emailAddress", "gotoyuzo@ruby-lang.org"], ["serialNumber", "123"], ] name = OpenSSL::X509::Name.new(dn) ary = name.to_a assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo@ruby-lang.org/serialNumber=123", name.to_s) assert_equal("DC", ary[0][0]) assert_equal("DC", ary[1][0]) assert_equal("CN", ary[2][0]) assert_equal("emailAddress", ary[3][0]) assert_equal("serialNumber", ary[4][0]) assert_equal("org", ary[0][1]) assert_equal("ruby-lang", ary[1][1]) assert_equal("GOTOU Yuuzou", ary[2][1]) assert_equal("gotoyuzo@ruby-lang.org", ary[3][1]) assert_equal("123", ary[4][1]) assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2]) assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2]) assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2]) assert_equal(OpenSSL::ASN1::IA5STRING, ary[3][2]) assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[4][2]) name_from_der = OpenSSL::X509::Name.new(name.to_der) assert_equal(name_from_der.to_s, name.to_s) assert_equal(name_from_der.to_a, name.to_a) assert_equal(name_from_der.to_der, name.to_der) end def test_unrecognized_oid dn = [ ["1.2.3.4.5.6.7.8.9.7.5.3.1", "Unknown OID 1"], ["1.1.2.3.5.8.13.21.34", "Unknown OID 2"], ["C", "US"], ["postalCode", "60602"], ["ST", "Illinois"], ["L", "Chicago"], #["street", "123 Fake St"], ["O", "Some Company LLC"], ["CN", "mydomain.com"] ] name = OpenSSL::X509::Name.new(dn) ary = name.to_a #assert_equal("/1.2.3.4.5.6.7.8.9.7.5.3.1=Unknown OID 1/1.1.2.3.5.8.13.21.34=Unknown OID 2/C=US/postalCode=60602/ST=Illinois/L=Chicago/street=123 Fake St/O=Some Company LLC/CN=mydomain.com", name.to_s) assert_equal("/1.2.3.4.5.6.7.8.9.7.5.3.1=Unknown OID 1/1.1.2.3.5.8.13.21.34=Unknown OID 2/C=US/postalCode=60602/ST=Illinois/L=Chicago/O=Some Company LLC/CN=mydomain.com", name.to_s) assert_equal("1.2.3.4.5.6.7.8.9.7.5.3.1", ary[0][0]) assert_equal("1.1.2.3.5.8.13.21.34", ary[1][0]) assert_equal("C", ary[2][0]) assert_equal("postalCode", ary[3][0]) assert_equal("ST", ary[4][0]) assert_equal("L", ary[5][0]) #assert_equal("street", ary[6][0]) assert_equal("O", ary[6][0]) assert_equal("CN", ary[7][0]) assert_equal("Unknown OID 1", ary[0][1]) assert_equal("Unknown OID 2", ary[1][1]) assert_equal("US", ary[2][1]) assert_equal("60602", ary[3][1]) assert_equal("Illinois", ary[4][1]) assert_equal("Chicago", ary[5][1]) #assert_equal("123 Fake St", ary[6][1]) assert_equal("Some Company LLC", ary[6][1]) assert_equal("mydomain.com", ary[7][1]) end def test_unrecognized_oid_parse_encode_equality dn = [ ["1.2.3.4.5.6.7.8.9.7.5.3.2", "Unknown OID1"], ["1.1.2.3.5.8.13.21.35", "Unknown OID2"], ["C", "US"], ["postalCode", "60602"], ["ST", "Illinois"], ["L", "Chicago"], #["street", "123 Fake St"], ["O", "Some Company LLC"], ["CN", "mydomain.com"] ] name1 = OpenSSL::X509::Name.new(dn) name2 = OpenSSL::X509::Name.parse(name1.to_s) assert_equal(name1.to_s, name2.to_s) assert_equal(name1.to_a, name2.to_a) end def test_s_parse dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org" name = OpenSSL::X509::Name.parse(dn) assert_equal(dn, name.to_s) ary = name.to_a assert_equal("DC", ary[0][0]) assert_equal("DC", ary[1][0]) assert_equal("CN", ary[2][0]) assert_equal("org", ary[0][1]) assert_equal("ruby-lang", ary[1][1]) assert_equal("www.ruby-lang.org", ary[2][1]) assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2]) assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2]) assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2]) dn2 = "DC=org, DC=ruby-lang, CN=www.ruby-lang.org" name = OpenSSL::X509::Name.parse(dn2) ary = name.to_a assert_equal(dn, name.to_s) assert_equal("org", ary[0][1]) assert_equal("ruby-lang", ary[1][1]) assert_equal("www.ruby-lang.org", ary[2][1]) name = OpenSSL::X509::Name.parse(dn2, @obj_type_tmpl) ary = name.to_a assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2]) assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2]) assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[2][2]) end def test_s_parse_rfc2253 scanner = OpenSSL::X509::Name::RFC2253DN.method(:scan) assert_equal([["C", "JP"]], scanner.call("C=JP")) assert_equal([ ["DC", "org"], ["DC", "ruby-lang"], ["CN", "GOTOU Yuuzou"], ["emailAddress", "gotoyuzo@ruby-lang.org"], ], scanner.call( "emailAddress=gotoyuzo@ruby-lang.org,CN=GOTOU Yuuzou,"+ "DC=ruby-lang,DC=org") ) u8 = OpenSSL::ASN1::UTF8STRING assert_equal([ ["DC", "org"], ["DC", "ruby-lang"], ["O", ",=+<>#;"], ["O", ",=+<>#;"], ["OU", ""], ["OU", ""], ["L", "aaa=\"bbb, ccc\""], ["L", "aaa=\"bbb, ccc\""], ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"], ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"], ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265"], ["CN", "\345\276\214\350\227\244\350\243\225\350\224\265", u8], ["2.5.4.3", "GOTOU, Yuuzou"], ["2.5.4.3", "GOTOU, Yuuzou"], ["2.5.4.3", "GOTOU, Yuuzou"], ["2.5.4.3", "GOTOU, Yuuzou"], ["CN", "GOTOU \"gotoyuzo\" Yuuzou"], ["CN", "GOTOU \"gotoyuzo\" Yuuzou"], ["1.2.840.113549.1.9.1", "gotoyuzo@ruby-lang.org"], ["emailAddress", "gotoyuzo@ruby-lang.org"], ], scanner.call( "emailAddress=gotoyuzo@ruby-lang.org," + "1.2.840.113549.1.9.1=gotoyuzo@ruby-lang.org," + 'CN=GOTOU \"gotoyuzo\" Yuuzou,' + 'CN="GOTOU \"gotoyuzo\" Yuuzou",' + '2.5.4.3=GOTOU\,\20Yuuzou,' + '2.5.4.3=GOTOU\, Yuuzou,' + '2.5.4.3="GOTOU, Yuuzou",' + '2.5.4.3="GOTOU\, Yuuzou",' + "CN=#0C0CE5BE8CE897A4E8A395E894B5," + 'CN=\E5\BE\8C\E8\97\A4\E8\A3\95\E8\94\B5,' + "CN=\"\xE5\xBE\x8C\xE8\x97\xA4\xE8\xA3\x95\xE8\x94\xB5\"," + "CN=\xE5\xBE\x8C\xE8\x97\xA4\xE8\xA3\x95\xE8\x94\xB5," + 'L=aaa\=\"bbb\, ccc\",' + 'L="aaa=\"bbb, ccc\"",' + 'OU=,' + 'OU="",' + 'O=\,\=\+\<\>\#\;,' + 'O=",=+<>#;",' + "DC=ruby-lang," + "DC=org") ) [ "DC=org+DC=jp", "DC=org,DC=ruby-lang+DC=rubyist,DC=www" ].each{|dn| ex = scanner.call(dn) rescue $! dn_r = Regexp.escape(dn) assert_match(/^multi-valued RDN is not supported: #{dn_r}/, ex.message) } [ ["DC=org,DC=exapmle,CN", "CN"], ["DC=org,DC=example,", ""], ["DC=org,DC=exapmle,CN=www.example.org;", "CN=www.example.org;"], ["DC=org,DC=exapmle,CN=#www.example.org", "CN=#www.example.org"], ["DC=org,DC=exapmle,CN=#777777.example.org", "CN=#777777.example.org"], ["DC=org,DC=exapmle,CN=\"www.example\".org", "CN=\"www.example\".org"], ["DC=org,DC=exapmle,CN=www.\"example.org\"", "CN=www.\"example.org\""], ["DC=org,DC=exapmle,CN=www.\"example\".org", "CN=www.\"example\".org"], ].each{|dn, msg| ex = scanner.call(dn) rescue $! assert_match(/^malformed RDN: .*=>#{Regexp.escape(msg)}/, ex.message) } dn = "CN=www.ruby-lang.org,DC=ruby-lang,DC=org" name = OpenSSL::X509::Name.parse_rfc2253(dn) assert_equal(dn, name.to_s(OpenSSL::X509::Name::RFC2253)) ary = name.to_a assert_equal("DC", ary[0][0]) assert_equal("DC", ary[1][0]) assert_equal("CN", ary[2][0]) assert_equal("org", ary[0][1]) assert_equal("ruby-lang", ary[1][1]) assert_equal("www.ruby-lang.org", ary[2][1]) assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2]) assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2]) assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2]) end def test_add_entry dn = [ ["DC", "org"], ["DC", "ruby-lang"], ["CN", "GOTOU Yuuzou"], ["emailAddress", "gotoyuzo@ruby-lang.org"], ["serialNumber", "123"], ] name = OpenSSL::X509::Name.new dn.each{|attr| name.add_entry(*attr) } ary = name.to_a assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo@ruby-lang.org/serialNumber=123", name.to_s) assert_equal("DC", ary[0][0]) assert_equal("DC", ary[1][0]) assert_equal("CN", ary[2][0]) assert_equal("emailAddress", ary[3][0]) assert_equal("serialNumber", ary[4][0]) assert_equal("org", ary[0][1]) assert_equal("ruby-lang", ary[1][1]) assert_equal("GOTOU Yuuzou", ary[2][1]) assert_equal("gotoyuzo@ruby-lang.org", ary[3][1]) assert_equal("123", ary[4][1]) assert_equal(OpenSSL::ASN1::IA5STRING, ary[0][2]) assert_equal(OpenSSL::ASN1::IA5STRING, ary[1][2]) assert_equal(OpenSSL::ASN1::UTF8STRING, ary[2][2]) assert_equal(OpenSSL::ASN1::IA5STRING, ary[3][2]) assert_equal(OpenSSL::ASN1::PRINTABLESTRING, ary[4][2]) end def test_add_entry_street # openssl/crypto/objects/obj_mac.h 1.83 dn = [ ["DC", "org"], ["DC", "ruby-lang"], ["CN", "GOTOU Yuuzou"], ["emailAddress", "gotoyuzo@ruby-lang.org"], ["serialNumber", "123"], ["street", "Namiki"], ] name = OpenSSL::X509::Name.new dn.each{|attr| name.add_entry(*attr) } ary = name.to_a assert_equal("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou/emailAddress=gotoyuzo@ruby-lang.org/serialNumber=123/street=Namiki", name.to_s) assert_equal("Namiki", ary[5][1]) end def test_to_s dn = [ ["DC", "org"], ["DC", "ruby-lang"], ["CN", "フー, バー"], ] name = OpenSSL::X509::Name.new dn.each { |x| name.add_entry(*x) } assert_equal "/DC=org/DC=ruby-lang/" \ "CN=\\xE3\\x83\\x95\\xE3\\x83\\xBC, \\xE3\\x83\\x90\\xE3\\x83\\xBC", name.to_s # OpenSSL escapes characters with MSB by default assert_equal \ "CN=\\E3\\83\\95\\E3\\83\\BC\\, \\E3\\83\\90\\E3\\83\\BC," \ "DC=ruby-lang,DC=org", name.to_s(OpenSSL::X509::Name::RFC2253) assert_equal "DC = org, DC = ruby-lang, " \ "CN = \"\\E3\\83\\95\\E3\\83\\BC, \\E3\\83\\90\\E3\\83\\BC\"", name.to_s(OpenSSL::X509::Name::ONELINE) empty = OpenSSL::X509::Name.new assert_equal "", empty.to_s assert_equal "", empty.to_s(OpenSSL::X509::Name::COMPAT) assert_equal "", empty.to_s(OpenSSL::X509::Name::RFC2253) assert_equal "", empty.to_s(OpenSSL::X509::Name::ONELINE) end def test_equals2 n1 = OpenSSL::X509::Name.parse 'CN=a' n2 = OpenSSL::X509::Name.parse 'CN=a' assert_equal n1, n2 end def test_spaceship n1 = OpenSSL::X509::Name.new([["CN", "a"]]) n2 = OpenSSL::X509::Name.new([["CN", "a"]]) n3 = OpenSSL::X509::Name.new([["CN", "ab"]]) assert_equal 0, n1 <=> n2 assert_equal -1, n1 <=> n3 assert_equal 0, n2 <=> n1 assert_equal -1, n2 <=> n3 assert_equal 1, n3 <=> n1 assert_equal 1, n3 <=> n2 end def name_hash(name) # OpenSSL 1.0.0 uses SHA1 for canonical encoding (not just a der) of # X509Name for X509_NAME_hash. name.respond_to?(:hash_old) ? name.hash_old : name.hash end def test_hash dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org" name = OpenSSL::X509::Name.parse(dn) d = OpenSSL::Digest::MD5.digest(name.to_der) expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24 assert_equal(expected, name_hash(name)) # dn = "/DC=org/DC=ruby-lang/CN=baz.ruby-lang.org" name = OpenSSL::X509::Name.parse(dn) d = OpenSSL::Digest::MD5.digest(name.to_der) expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24 assert_equal(expected, name_hash(name)) end def test_equality name0 = OpenSSL::X509::Name.new([["DC", "org"], ["DC", "ruby-lang"], ["CN", "bar.ruby-lang.org"]]) name1 = OpenSSL::X509::Name.new([["DC", "org"], ["DC", "ruby-lang"], ["CN", "bar.ruby-lang.org"]]) name2 = OpenSSL::X509::Name.new([["DC", "org"], ["DC", "ruby-lang"], ["CN", "baz.ruby-lang.org"]]) assert_equal true, name0 == name1 assert_equal true, name0.eql?(name1) assert_equal false, name0 == name2 assert_equal false, name0.eql?(name2) end def test_dup name = OpenSSL::X509::Name.parse("/CN=ruby-lang.org") assert_equal(name.to_der, name.dup.to_der) end end end openssl-2.0.9/test/test_x509req.rb000066400000000000000000000127311336157045000167750ustar00rootroot00000000000000# frozen_string_literal: false require_relative "utils" if defined?(OpenSSL::TestUtils) class OpenSSL::TestX509Request < OpenSSL::TestCase def setup super @rsa1024 = Fixtures.pkey("rsa1024") @rsa2048 = Fixtures.pkey("rsa2048") @dsa256 = Fixtures.pkey("dsa256") @dsa512 = Fixtures.pkey("dsa512") @dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=GOTOU Yuuzou") end def issue_csr(ver, dn, key, digest) req = OpenSSL::X509::Request.new req.version = ver req.subject = dn req.public_key = key.public_key req.sign(key, digest) req end def test_public_key req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der) req = OpenSSL::X509::Request.new(req.to_der) assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der) req = issue_csr(0, @dn, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new) assert_equal(@dsa512.public_key.to_der, req.public_key.to_der) req = OpenSSL::X509::Request.new(req.to_der) assert_equal(@dsa512.public_key.to_der, req.public_key.to_der) end def test_version req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) assert_equal(0, req.version) req = OpenSSL::X509::Request.new(req.to_der) assert_equal(0, req.version) req = issue_csr(1, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) assert_equal(1, req.version) req = OpenSSL::X509::Request.new(req.to_der) assert_equal(1, req.version) end def test_subject req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) assert_equal(@dn.to_der, req.subject.to_der) req = OpenSSL::X509::Request.new(req.to_der) assert_equal(@dn.to_der, req.subject.to_der) end def create_ext_req(exts) ef = OpenSSL::X509::ExtensionFactory.new exts = exts.collect{|e| ef.create_extension(*e) } return OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence(exts)]) end def get_ext_req(ext_req_value) set = OpenSSL::ASN1.decode(ext_req_value) seq = set.value[0] seq.value.collect{|asn1ext| OpenSSL::X509::Extension.new(asn1ext).to_a } end def test_attr exts = [ ["keyUsage", "Digital Signature, Key Encipherment", true], ["subjectAltName", "email:gotoyuzo@ruby-lang.org", false], ] attrval = create_ext_req(exts) attrs = [ OpenSSL::X509::Attribute.new("extReq", attrval), OpenSSL::X509::Attribute.new("msExtReq", attrval), ] req0 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) attrs.each{|attr| req0.add_attribute(attr) } req1 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) req1.attributes = attrs assert_equal(req0.to_der, req1.to_der) attrs = req0.attributes assert_equal(2, attrs.size) assert_equal("extReq", attrs[0].oid) assert_equal("msExtReq", attrs[1].oid) assert_equal(exts, get_ext_req(attrs[0].value)) assert_equal(exts, get_ext_req(attrs[1].value)) req = OpenSSL::X509::Request.new(req0.to_der) attrs = req.attributes assert_equal(2, attrs.size) assert_equal("extReq", attrs[0].oid) assert_equal("msExtReq", attrs[1].oid) assert_equal(exts, get_ext_req(attrs[0].value)) assert_equal(exts, get_ext_req(attrs[1].value)) end def test_sign_and_verify_rsa_sha1 req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) assert_equal(true, req.verify(@rsa1024)) assert_equal(false, req.verify(@rsa2048)) assert_equal(false, request_error_returns_false { req.verify(@dsa256) }) assert_equal(false, request_error_returns_false { req.verify(@dsa512) }) req.version = 1 assert_equal(false, req.verify(@rsa1024)) end def test_sign_and_verify_rsa_md5 req = issue_csr(0, @dn, @rsa2048, OpenSSL::Digest::MD5.new) assert_equal(false, req.verify(@rsa1024)) assert_equal(true, req.verify(@rsa2048)) assert_equal(false, request_error_returns_false { req.verify(@dsa256) }) assert_equal(false, request_error_returns_false { req.verify(@dsa512) }) req.subject = OpenSSL::X509::Name.parse("/C=JP/CN=FooBar") assert_equal(false, req.verify(@rsa2048)) rescue OpenSSL::X509::RequestError # RHEL7 disables MD5 end def test_sign_and_verify_dsa req = issue_csr(0, @dn, @dsa512, OpenSSL::TestUtils::DSA_SIGNATURE_DIGEST.new) assert_equal(false, request_error_returns_false { req.verify(@rsa1024) }) assert_equal(false, request_error_returns_false { req.verify(@rsa2048) }) assert_equal(false, req.verify(@dsa256)) assert_equal(true, req.verify(@dsa512)) req.public_key = @rsa1024.public_key assert_equal(false, req.verify(@dsa512)) end def test_sign_and_verify_rsa_dss1 req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::DSS1.new) assert_equal(true, req.verify(@rsa1024)) assert_equal(false, req.verify(@rsa2048)) assert_equal(false, request_error_returns_false { req.verify(@dsa256) }) assert_equal(false, request_error_returns_false { req.verify(@dsa512) }) req.version = 1 assert_equal(false, req.verify(@rsa1024)) rescue OpenSSL::X509::RequestError pend end if defined?(OpenSSL::Digest::DSS1) def test_sign_and_verify_dsa_md5 assert_raise(OpenSSL::X509::RequestError){ issue_csr(0, @dn, @dsa512, OpenSSL::Digest::MD5.new) } end def test_dup req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new) assert_equal(req.to_der, req.dup.to_der) end private def request_error_returns_false yield rescue OpenSSL::X509::RequestError false end end end openssl-2.0.9/test/test_x509store.rb000066400000000000000000000232431336157045000173420ustar00rootroot00000000000000# frozen_string_literal: false require_relative "utils" if defined?(OpenSSL::TestUtils) class OpenSSL::TestX509Store < OpenSSL::TestCase def setup super @rsa1024 = Fixtures.pkey("rsa1024") @rsa2048 = Fixtures.pkey("rsa2048") @dsa256 = Fixtures.pkey("dsa256") @dsa512 = Fixtures.pkey("dsa512") @ca1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA1") @ca2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA2") @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1") @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2") end def test_nosegv_on_cleanup cert = OpenSSL::X509::Certificate.new store = OpenSSL::X509::Store.new ctx = OpenSSL::X509::StoreContext.new(store, cert, []) EnvUtil.suppress_warning do ctx.cleanup end ctx.verify end def test_add_file ca_exts = [ ["basicConstraints", "CA:TRUE", true], ["keyUsage", "cRLSign,keyCertSign", true], ] cert1 = issue_cert(@ca1, @rsa1024, 1, ca_exts, nil, nil) cert2 = issue_cert(@ca2, @rsa2048, 1, ca_exts, nil, nil) tmpfile = Tempfile.open { |f| f << cert1.to_pem << cert2.to_pem; f } store = OpenSSL::X509::Store.new assert_equal false, store.verify(cert1) assert_equal false, store.verify(cert2) store.add_file(tmpfile.path) assert_equal true, store.verify(cert1) assert_equal true, store.verify(cert2) # OpenSSL < 1.1.1 leaks an error on a duplicate certificate assert_nothing_raised { store.add_file(tmpfile.path) } assert_equal [], OpenSSL.errors ensure tmpfile and tmpfile.close! end def test_verify # OpenSSL uses time(2) while Time.now uses clock_gettime(CLOCK_REALTIME), # and there may be difference. now = Time.now - 3 ca_exts = [ ["basicConstraints","CA:TRUE",true], ["keyUsage","cRLSign,keyCertSign",true], ] ee_exts = [ ["keyUsage","keyEncipherment,digitalSignature",true], ] ca1_cert = issue_cert(@ca1, @rsa2048, 1, ca_exts, nil, nil) ca2_cert = issue_cert(@ca2, @rsa1024, 2, ca_exts, ca1_cert, @rsa2048, not_after: now+1800) ee1_cert = issue_cert(@ee1, @dsa256, 10, ee_exts, ca2_cert, @rsa1024) ee2_cert = issue_cert(@ee2, @dsa512, 20, ee_exts, ca2_cert, @rsa1024) ee3_cert = issue_cert(@ee2, @dsa512, 30, ee_exts, ca2_cert, @rsa1024, not_before: now-100, not_after: now-1) ee4_cert = issue_cert(@ee2, @dsa512, 40, ee_exts, ca2_cert, @rsa1024, not_before: now+1000, not_after: now+2000,) revoke_info = [] crl1 = issue_crl(revoke_info, 1, now, now+1800, [], ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new) revoke_info = [ [2, now, 1], ] crl1_2 = issue_crl(revoke_info, 2, now, now+1800, [], ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new) revoke_info = [ [20, now, 1], ] crl2 = issue_crl(revoke_info, 1, now, now+1800, [], ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new) revoke_info = [] crl2_2 = issue_crl(revoke_info, 2, now-100, now-1, [], ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new) assert_equal(true, ca1_cert.verify(ca1_cert.public_key)) # self signed assert_equal(true, ca2_cert.verify(ca1_cert.public_key)) # issued by ca1 assert_equal(true, ee1_cert.verify(ca2_cert.public_key)) # issued by ca2 assert_equal(true, ee2_cert.verify(ca2_cert.public_key)) # issued by ca2 assert_equal(true, ee3_cert.verify(ca2_cert.public_key)) # issued by ca2 assert_equal(true, crl1.verify(ca1_cert.public_key)) # issued by ca1 assert_equal(true, crl1_2.verify(ca1_cert.public_key)) # issued by ca1 assert_equal(true, crl2.verify(ca2_cert.public_key)) # issued by ca2 assert_equal(true, crl2_2.verify(ca2_cert.public_key)) # issued by ca2 store = OpenSSL::X509::Store.new assert_equal(false, store.verify(ca1_cert)) assert_not_equal(OpenSSL::X509::V_OK, store.error) assert_equal(false, store.verify(ca2_cert)) assert_not_equal(OpenSSL::X509::V_OK, store.error) store.add_cert(ca1_cert) assert_equal(true, store.verify(ca2_cert)) assert_equal(OpenSSL::X509::V_OK, store.error) assert_equal("ok", store.error_string) chain = store.chain assert_equal(2, chain.size) assert_equal(@ca2.to_der, chain[0].subject.to_der) assert_equal(@ca1.to_der, chain[1].subject.to_der) store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT assert_equal(false, store.verify(ca2_cert)) assert_not_equal(OpenSSL::X509::V_OK, store.error) store.purpose = OpenSSL::X509::PURPOSE_CRL_SIGN assert_equal(true, store.verify(ca2_cert)) assert_equal(OpenSSL::X509::V_OK, store.error) store.add_cert(ca2_cert) store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT assert_equal(true, store.verify(ee1_cert)) assert_equal(true, store.verify(ee2_cert)) assert_equal(OpenSSL::X509::V_OK, store.error) assert_equal("ok", store.error_string) chain = store.chain assert_equal(3, chain.size) assert_equal(@ee2.to_der, chain[0].subject.to_der) assert_equal(@ca2.to_der, chain[1].subject.to_der) assert_equal(@ca1.to_der, chain[2].subject.to_der) assert_equal(false, store.verify(ee3_cert)) assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error) assert_match(/expire/i, store.error_string) assert_equal(false, store.verify(ee4_cert)) assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error) assert_match(/not yet valid/i, store.error_string) store = OpenSSL::X509::Store.new store.add_cert(ca1_cert) store.add_cert(ca2_cert) store.time = now + 1500 assert_equal(true, store.verify(ca1_cert)) assert_equal(true, store.verify(ca2_cert)) assert_equal(true, store.verify(ee4_cert)) store.time = now + 1900 assert_equal(true, store.verify(ca1_cert)) assert_equal(false, store.verify(ca2_cert)) assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error) assert_equal(false, store.verify(ee4_cert)) assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error) store.time = now + 4000 assert_equal(false, store.verify(ee1_cert)) assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error) assert_equal(false, store.verify(ee4_cert)) assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error) # the underlying X509 struct caches the result of the last # verification for signature and not-before. so the following code # rebuilds new objects to avoid site effect. store.time = Time.now - 4000 assert_equal(false, store.verify(OpenSSL::X509::Certificate.new(ca2_cert))) assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error) assert_equal(false, store.verify(OpenSSL::X509::Certificate.new(ee1_cert))) assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error) store = OpenSSL::X509::Store.new store.purpose = OpenSSL::X509::PURPOSE_ANY store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK store.add_cert(ca1_cert) store.add_crl(crl1) # revoke no cert store.add_crl(crl2) # revoke ee2_cert assert_equal(true, store.verify(ca1_cert)) assert_equal(true, store.verify(ca2_cert)) assert_equal(true, store.verify(ee1_cert, [ca2_cert])) assert_equal(false, store.verify(ee2_cert, [ca2_cert])) store = OpenSSL::X509::Store.new store.purpose = OpenSSL::X509::PURPOSE_ANY store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK store.add_cert(ca1_cert) store.add_crl(crl1_2) # revoke ca2_cert store.add_crl(crl2) # revoke ee2_cert assert_equal(true, store.verify(ca1_cert)) assert_equal(false, store.verify(ca2_cert)) assert_equal(true, store.verify(ee1_cert, [ca2_cert]), "This test is expected to be success with OpenSSL 0.9.7c or later.") assert_equal(false, store.verify(ee2_cert, [ca2_cert])) store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL assert_equal(true, store.verify(ca1_cert)) assert_equal(false, store.verify(ca2_cert)) assert_equal(false, store.verify(ee1_cert, [ca2_cert])) assert_equal(false, store.verify(ee2_cert, [ca2_cert])) store = OpenSSL::X509::Store.new store.purpose = OpenSSL::X509::PURPOSE_ANY store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL store.add_cert(ca1_cert) store.add_cert(ca2_cert) store.add_crl(crl1) store.add_crl(crl2_2) # issued by ca2 but expired. assert_equal(true, store.verify(ca1_cert)) assert_equal(true, store.verify(ca2_cert)) assert_equal(false, store.verify(ee1_cert)) assert_equal(OpenSSL::X509::V_ERR_CRL_HAS_EXPIRED, store.error) assert_equal(false, store.verify(ee2_cert)) end def test_set_errors return if openssl?(1, 1, 0) || libressl? now = Time.now ca1_cert = issue_cert(@ca1, @rsa2048, 1, [], nil, nil) store = OpenSSL::X509::Store.new store.add_cert(ca1_cert) assert_raise(OpenSSL::X509::StoreError){ store.add_cert(ca1_cert) # add same certificate twice } revoke_info = [] crl1 = issue_crl(revoke_info, 1, now, now+1800, [], ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new) revoke_info = [ [2, now, 1], ] crl2 = issue_crl(revoke_info, 2, now+1800, now+3600, [], ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new) store.add_crl(crl1) assert_raise(OpenSSL::X509::StoreError){ store.add_crl(crl2) # add CRL issued by same CA twice. } end def test_dup store = OpenSSL::X509::Store.new assert_raise(NoMethodError) { store.dup } ctx = OpenSSL::X509::StoreContext.new(store) assert_raise(NoMethodError) { ctx.dup } end end end openssl-2.0.9/test/ut_eof.rb000066400000000000000000000054641336157045000160070ustar00rootroot00000000000000# frozen_string_literal: false require 'test/unit' if defined?(OpenSSL::TestUtils) module OpenSSL::TestEOF def test_eof_0 open_file("") {|f| assert_equal("", f.read(0)) assert_equal("", f.read(0)) assert_equal("", f.read) assert_equal("", f.read(0)) assert_equal("", f.read(0)) } open_file("") {|f| assert_nil(f.read(1)) assert_equal("", f.read) assert_nil(f.read(1)) } open_file("") {|f| s = "x" assert_equal("", f.read(nil, s)) assert_equal("", s) } open_file("") {|f| s = "x" assert_nil(f.read(10, s)) assert_equal("", s) } end def test_eof_0_rw return unless respond_to? :open_file_rw open_file_rw("") {|f| assert_equal("", f.read) assert_equal("", f.read) assert_equal(0, f.syswrite("")) assert_equal("", f.read) } end def test_eof_1 open_file("a") {|f| assert_equal("", f.read(0)) assert_equal("a", f.read(1)) assert_equal("" , f.read(0)) assert_equal("" , f.read(0)) assert_equal("", f.read) assert_equal("", f.read(0)) assert_equal("", f.read(0)) } open_file("a") {|f| assert_equal("a", f.read(1)) assert_nil(f.read(1)) } open_file("a") {|f| assert_equal("a", f.read(2)) assert_nil(f.read(1)) assert_equal("", f.read) assert_nil(f.read(1)) } open_file("a") {|f| assert_equal("a", f.read) assert_nil(f.read(1)) assert_equal("", f.read) assert_nil(f.read(1)) } open_file("a") {|f| assert_equal("a", f.read(2)) assert_equal("", f.read) assert_equal("", f.read) } open_file("a") {|f| assert_equal("a", f.read) assert_equal("", f.read(0)) } open_file("a") {|f| s = "x" assert_equal("a", f.read(nil, s)) assert_equal("a", s) } open_file("a") {|f| s = "x" assert_equal("a", f.read(10, s)) assert_equal("a", s) } end def test_eof_2 open_file("") {|f| assert_equal("", f.read) assert_predicate(f, :eof?) } end def test_eof_3 open_file("") {|f| assert_predicate(f, :eof?) } end module Seek def open_file_seek(content, pos) open_file(content) do |f| f.seek(pos) yield f end end def test_eof_0_seek open_file_seek("", 10) {|f| assert_equal(10, f.pos) assert_equal("", f.read(0)) assert_equal("", f.read) assert_equal("", f.read(0)) assert_equal("", f.read) } end def test_eof_1_seek open_file_seek("a", 10) {|f| assert_equal("", f.read) assert_equal("", f.read) } open_file_seek("a", 1) {|f| assert_equal("", f.read) assert_equal("", f.read) } end end end end openssl-2.0.9/test/utils.rb000066400000000000000000000223571336157045000156660ustar00rootroot00000000000000# frozen_string_literal: false begin require "openssl" # Disable FIPS mode for tests for installations # where FIPS mode would be enabled by default. # Has no effect on all other installations. OpenSSL.fips_mode=false rescue LoadError end # Compile OpenSSL with crypto-mdebug and run this test suite with OSSL_MDEBUG=1 # environment variable to enable memory leak check. if ENV["OSSL_MDEBUG"] == "1" if OpenSSL.respond_to?(:print_mem_leaks) OpenSSL.mem_check_start END { GC.start case OpenSSL.print_mem_leaks when nil warn "mdebug: check what is printed" when true raise "mdebug: memory leaks detected" end } else warn "OSSL_MDEBUG=1 is specified but OpenSSL is not built with crypto-mdebug" end end require "test/unit" require "tempfile" require "socket" require "envutil" if defined?(OpenSSL) && OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10000000 module OpenSSL::TestUtils module Fixtures module_function def pkey(name) OpenSSL::PKey.read(read_file("pkey", name)) end def pkey_dh(name) # DH parameters can be read by OpenSSL::PKey.read atm OpenSSL::PKey::DH.new(read_file("pkey", name)) end def read_file(category, name) @file_cache ||= {} @file_cache[[category, name]] ||= File.read(File.join(__dir__, "fixtures", category, name + ".pem")) end end DSA_SIGNATURE_DIGEST = OpenSSL::OPENSSL_VERSION_NUMBER > 0x10000000 ? OpenSSL::Digest::SHA1 : OpenSSL::Digest::DSS1 module_function def issue_cert(dn, key, serial, extensions, issuer, issuer_key, not_before: nil, not_after: nil, digest: nil) cert = OpenSSL::X509::Certificate.new issuer = cert unless issuer issuer_key = key unless issuer_key cert.version = 2 cert.serial = serial cert.subject = dn cert.issuer = issuer.subject cert.public_key = key.public_key now = Time.now cert.not_before = not_before || now - 3600 cert.not_after = not_after || now + 3600 ef = OpenSSL::X509::ExtensionFactory.new ef.subject_certificate = cert ef.issuer_certificate = issuer extensions.each{|oid, value, critical| cert.add_extension(ef.create_extension(oid, value, critical)) } digest ||= OpenSSL::PKey::DSA === issuer_key ? DSA_SIGNATURE_DIGEST.new : "sha256" cert.sign(issuer_key, digest) cert end def issue_crl(revoke_info, serial, lastup, nextup, extensions, issuer, issuer_key, digest) crl = OpenSSL::X509::CRL.new crl.issuer = issuer.subject crl.version = 1 crl.last_update = lastup crl.next_update = nextup revoke_info.each{|rserial, time, reason_code| revoked = OpenSSL::X509::Revoked.new revoked.serial = rserial revoked.time = time enum = OpenSSL::ASN1::Enumerated(reason_code) ext = OpenSSL::X509::Extension.new("CRLReason", enum) revoked.add_extension(ext) crl.add_revoked(revoked) } ef = OpenSSL::X509::ExtensionFactory.new ef.issuer_certificate = issuer ef.crl = crl crlnum = OpenSSL::ASN1::Integer(serial) crl.add_extension(OpenSSL::X509::Extension.new("crlNumber", crlnum)) extensions.each{|oid, value, critical| crl.add_extension(ef.create_extension(oid, value, critical)) } crl.sign(issuer_key, digest) crl end def get_subject_key_id(cert) asn1_cert = OpenSSL::ASN1.decode(cert) tbscert = asn1_cert.value[0] pkinfo = tbscert.value[6] publickey = pkinfo.value[1] pkvalue = publickey.value OpenSSL::Digest::SHA1.hexdigest(pkvalue).scan(/../).join(":").upcase end def openssl?(major = nil, minor = nil, fix = nil, patch = 0) return false if OpenSSL::OPENSSL_VERSION.include?("LibreSSL") return true unless major OpenSSL::OPENSSL_VERSION_NUMBER >= major * 0x10000000 + minor * 0x100000 + fix * 0x1000 + patch * 0x10 end def libressl?(major = nil, minor = nil, fix = nil) version = OpenSSL::OPENSSL_VERSION.scan(/LibreSSL (\d+)\.(\d+)\.(\d+).*/)[0] return false unless version !major || (version.map(&:to_i) <=> [major, minor, fix]) >= 0 end end class OpenSSL::TestCase < Test::Unit::TestCase include OpenSSL::TestUtils extend OpenSSL::TestUtils def setup if ENV["OSSL_GC_STRESS"] == "1" GC.stress = true end end def teardown if ENV["OSSL_GC_STRESS"] == "1" GC.stress = false end # OpenSSL error stack must be empty assert_equal([], OpenSSL.errors) end end class OpenSSL::SSLTestCase < OpenSSL::TestCase RUBY = EnvUtil.rubybin ITERATIONS = ($0 == __FILE__) ? 100 : 10 def setup super @ca_key = Fixtures.pkey("rsa2048") @svr_key = Fixtures.pkey("rsa1024") @cli_key = Fixtures.pkey("rsa2048") @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA") @svr = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost") @cli = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost") ca_exts = [ ["basicConstraints","CA:TRUE",true], ["keyUsage","cRLSign,keyCertSign",true], ] ee_exts = [ ["keyUsage","keyEncipherment,digitalSignature",true], ] @ca_cert = issue_cert(@ca, @ca_key, 1, ca_exts, nil, nil) @svr_cert = issue_cert(@svr, @svr_key, 2, ee_exts, @ca_cert, @ca_key) @cli_cert = issue_cert(@cli, @cli_key, 3, ee_exts, @ca_cert, @ca_key) @server = nil end def tls12_supported? OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1_2) end def readwrite_loop(ctx, ssl) while line = ssl.gets ssl.write(line) end end def start_server(verify_mode: OpenSSL::SSL::VERIFY_NONE, start_immediately: true, ctx_proc: nil, server_proc: method(:readwrite_loop), ignore_listener_error: false, &block) IO.pipe {|stop_pipe_r, stop_pipe_w| store = OpenSSL::X509::Store.new store.add_cert(@ca_cert) store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT ctx = OpenSSL::SSL::SSLContext.new ctx.cert_store = store ctx.cert = @svr_cert ctx.key = @svr_key ctx.tmp_dh_callback = proc { Fixtures.pkey_dh("dh1024") } ctx.verify_mode = verify_mode ctx_proc.call(ctx) if ctx_proc Socket.do_not_reverse_lookup = true tcps = TCPServer.new("127.0.0.1", 0) port = tcps.connect_address.ip_port ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx) ssls.start_immediately = start_immediately threads = [] begin server_thread = Thread.new do if Thread.method_defined?(:report_on_exception=) # Ruby >= 2.4 Thread.current.report_on_exception = false end begin loop do begin readable, = IO.select([ssls, stop_pipe_r]) break if readable.include? stop_pipe_r ssl = ssls.accept rescue OpenSSL::SSL::SSLError, IOError, Errno::EBADF, Errno::EINVAL, Errno::ECONNABORTED, Errno::ENOTSOCK, Errno::ECONNRESET retry if ignore_listener_error raise end th = Thread.new do if Thread.method_defined?(:report_on_exception=) Thread.current.report_on_exception = false end begin server_proc.call(ctx, ssl) ensure ssl.close end true end threads << th end ensure tcps.close end end client_thread = Thread.new do if Thread.method_defined?(:report_on_exception=) Thread.current.report_on_exception = false end begin block.call(port) ensure # Stop accepting new connection stop_pipe_w.close server_thread.join end end threads.unshift client_thread ensure # Terminate existing connections. If a thread did 'pend', re-raise it. pend = nil threads.each { |th| begin th.join(10) or th.raise(RuntimeError, "[start_server] thread did not exit in 10 secs") rescue (defined?(MiniTest::Skip) ? MiniTest::Skip : Test::Unit::PendedError) # MiniTest::Skip is for the Ruby tree pend = $! rescue Exception end } raise pend if pend assert_join_threads(threads) end } end end class OpenSSL::PKeyTestCase < OpenSSL::TestCase def check_component(base, test, keys) keys.each { |comp| assert_equal base.send(comp), test.send(comp) } end def dup_public(key) case key when OpenSSL::PKey::RSA rsa = OpenSSL::PKey::RSA.new rsa.set_key(key.n, key.e, nil) rsa when OpenSSL::PKey::DSA dsa = OpenSSL::PKey::DSA.new dsa.set_pqg(key.p, key.q, key.g) dsa.set_key(key.pub_key, nil) dsa when OpenSSL::PKey::DH dh = OpenSSL::PKey::DH.new dh.set_pqg(key.p, nil, key.g) dh else if defined?(OpenSSL::PKey::EC) && OpenSSL::PKey::EC === key ec = OpenSSL::PKey::EC.new(key.group) ec.public_key = key.public_key ec else raise "unknown key type" end end end end end openssl-2.0.9/tool/000077500000000000000000000000001336157045000141665ustar00rootroot00000000000000openssl-2.0.9/tool/ruby-openssl-docker/000077500000000000000000000000001336157045000200755ustar00rootroot00000000000000openssl-2.0.9/tool/ruby-openssl-docker/Dockerfile000066400000000000000000000077661336157045000221070ustar00rootroot00000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ autoconf \ bison \ build-essential \ ca-certificates \ curl \ gzip \ libreadline-dev \ patch \ pkg-config \ sed \ zlib1g-dev # Supported OpenSSL versions: 1.0.1- RUN mkdir -p /build/openssl RUN curl -s https://www.openssl.org/source/openssl-1.0.0t.tar.gz | tar -C /build/openssl -xzf - && \ cd /build/openssl/openssl-1.0.0t && \ ./Configure \ --openssldir=/opt/openssl/openssl-1.0.0 \ shared linux-x86_64 && \ make && make install_sw RUN curl -s https://www.openssl.org/source/openssl-1.0.1u.tar.gz | tar -C /build/openssl -xzf - && \ cd /build/openssl/openssl-1.0.1u && \ ./Configure \ --openssldir=/opt/openssl/openssl-1.0.1 \ shared linux-x86_64 && \ make && make install_sw RUN curl -s https://www.openssl.org/source/openssl-1.0.2o.tar.gz | tar -C /build/openssl -xzf - && \ cd /build/openssl/openssl-1.0.2o && \ ./Configure \ --openssldir=/opt/openssl/openssl-1.0.2 \ shared linux-x86_64 && \ make && make install_sw RUN curl -s https://www.openssl.org/source/openssl-1.1.0h.tar.gz | tar -C /build/openssl -xzf - && \ cd /build/openssl/openssl-1.1.0h && \ ./Configure \ --prefix=/opt/openssl/openssl-1.1.0 \ enable-crypto-mdebug enable-crypto-mdebug-backtrace \ linux-x86_64 && \ make && make install_sw RUN curl -s https://www.openssl.org/source/openssl-1.1.1-pre8.tar.gz | tar -C /build/openssl -xzf - && \ cd /build/openssl/openssl-1.1.1-pre8 && \ ./Configure \ --prefix=/opt/openssl/openssl-1.1.1 \ enable-crypto-mdebug enable-crypto-mdebug-backtrace \ linux-x86_64 && \ make && make install_sw # Supported libressl versions: 2.3- RUN curl -s http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-2.3.10.tar.gz | tar -C /build/openssl -xzf - && \ cd /build/openssl/libressl-2.3.10 && \ ./configure \ --prefix=/opt/openssl/libressl-2.3 && \ make && make install RUN curl -s http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-2.4.5.tar.gz | tar -C /build/openssl -xzf - && \ cd /build/openssl/libressl-2.4.5 && \ ./configure \ --prefix=/opt/openssl/libressl-2.4 && \ make && make install RUN curl -s http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-2.5.5.tar.gz | tar -C /build/openssl -xzf - && \ cd /build/openssl/libressl-2.5.5 && \ ./configure \ --prefix=/opt/openssl/libressl-2.5 && \ make && make install RUN curl -s http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-2.6.5.tar.gz | tar -C /build/openssl -xzf - && \ cd /build/openssl/libressl-2.6.5 && \ ./configure \ --prefix=/opt/openssl/libressl-2.6 && \ make && make install RUN curl -s http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-2.7.4.tar.gz | tar -C /build/openssl -xzf - && \ cd /build/openssl/libressl-2.7.4 && \ ./configure \ --prefix=/opt/openssl/libressl-2.7 && \ make && make install # Supported Ruby versions: 2.3- RUN mkdir -p /build/ruby RUN curl -s https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.7.tar.gz | tar -C /build/ruby -xzf - && \ cd /build/ruby/ruby-2.3.7 && \ autoconf && ./configure \ --without-openssl \ --prefix=/opt/ruby/ruby-2.3 \ --disable-install-doc && \ make && make install RUN curl -s https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.4.tar.gz | tar -C /build/ruby -xzf - && \ cd /build/ruby/ruby-2.4.4 && \ autoconf && ./configure \ --without-openssl \ --prefix=/opt/ruby/ruby-2.4 \ --disable-install-doc && \ make && make install RUN curl -s https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.1.tar.gz | tar -C /build/ruby -xzf - && \ cd /build/ruby/ruby-2.5.1 && \ autoconf && ./configure \ --without-openssl \ --prefix=/opt/ruby/ruby-2.5 \ --disable-install-doc && \ make && make install ONBUILD ADD . /home/openssl/code ONBUILD WORKDIR /home/openssl/code COPY init.sh /home/openssl/init.sh ENTRYPOINT ["/home/openssl/init.sh"] openssl-2.0.9/tool/ruby-openssl-docker/README.md000066400000000000000000000003211336157045000213500ustar00rootroot00000000000000# ruby-openssl-docker Docker image for testing. The image contains various binaries of supported versions of OpenSSL, LibreSSL, and Ruby. CONTRIBUTING.md on the top directory describes how to use the image. openssl-2.0.9/tool/ruby-openssl-docker/init.sh000077500000000000000000000007131336157045000214000ustar00rootroot00000000000000#!/bin/bash if [[ "$RUBY_VERSION" = "" ]] then RUBY_VERSION=ruby-2.5 fi if [[ "$OPENSSL_VERSION" = "" ]] then OPENSSL_VERSION=openssl-1.1.0 fi echo "Using Ruby ${RUBY_VERSION} with OpenSSL ${OPENSSL_VERSION}." export PATH="/opt/ruby/${RUBY_VERSION}/bin:$PATH" export LD_LIBRARY_PATH="/opt/openssl/${OPENSSL_VERSION}/lib" export PKG_CONFIG_PATH="/opt/openssl/${OPENSSL_VERSION}/lib/pkgconfig" rake install_dependencies USE_HTTP_RUBYGEMS_ORG=1 exec $* openssl-2.0.9/tool/sync-with-trunk000077500000000000000000000076741336157045000172200ustar00rootroot00000000000000#!/bin/sh set -e # Pick changes from Ruby trunk and apply on this repository. # Note that Git >= 2.5 is required. sha1_to_rev() { git show -s --format=format:%B $1 | tail -n1 | grep -Po '(?<=@)[0-9]+'; } rev_to_sha1() { git log --format=format:%H -n1 --grep '^git-svn-id: .*@'$2' ' $1; } echo "#### Step 0. Fetch Ruby trunk" git remote | grep '^ruby$' >/dev/null || git remote add ruby https://github.com/ruby/ruby.git git fetch ruby BRANCH_EXTRACT=sync/extract echo "#### Step 1. Sync '$BRANCH_EXTRACT' with 'ruby/trunk'" [ "$(git branch --list $BRANCH_EXTRACT)" ] || git branch $BRANCH_EXTRACT ruby/trunk [ ! -d tmp/sync-extract ] && git worktree add tmp/sync-extract $BRANCH_EXTRACT ( cd tmp/sync-extract git checkout $BRANCH_EXTRACT if [ $(git rev-parse HEAD) = $(git rev-parse ruby/trunk) ]; then filter_range= else old_head=$(git rev-parse HEAD) echo "Updating '$BRANCH_EXTRACT'... HEAD was $old_head." graftpoint="$(rev_to_sha1 ruby/trunk $(sha1_to_rev $old_head)) $old_head" grep "^$graftpoint$" $(git rev-parse --git-common-dir)/info/grafts >/dev/null 2>&1 || echo "$graftpoint" >> $(git rev-parse --git-common-dir)/info/grafts git reset -q --hard ruby/trunk filter_range=$old_head..$BRANCH_EXTRACT fi echo "## Remove unrelated commits" git filter-branch -f --prune-empty --index-filter ' git rm --cached -qr --ignore-unmatch . && git reset -q $GIT_COMMIT -- ext/openssl test/openssl sample/openssl && git rm --cached -q --ignore-unmatch ext/openssl/depend ext/openssl/openssl.gemspec ' -- $filter_range ||: echo "## Adjust path" git filter-branch -f --prune-empty --index-filter ' git ls-files --stage | \ sed "s:\ttest/openssl:\ttest:" | \ sed "s:\text/openssl/lib:\tlib:" | \ sed "s:\text/openssl/sample:\tsample/openssl:" | \ sed "s:\tsample/openssl:\tsample:" | \ sed "s:\text/openssl/History.md:\tHistory.md:" | \ GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE" : ' -- $filter_range ||: echo "## Fix author/committer email" git filter-branch -f --env-filter ' export GIT_AUTHOR_EMAIL=${GIT_AUTHOR_EMAIL/b2dd03c8-39d4-4d8f-98ff-823fe69b080e/ruby-lang.org} export GIT_COMMITTER_EMAIL=${GIT_COMMITTER_EMAIL/b2dd03c8-39d4-4d8f-98ff-823fe69b080e/ruby-lang.org} ' -- $filter_range ||: [ "$graftpoint" ] && sed -ie "/^$graftpoint$/d" $(git rev-parse --git-common-dir)/info/grafts ) LAST_SYNC_COMMIT=$(git log --format=format:%H -n1 --grep '^Sync-with-trunk: r') LAST_SYNC_REV=$(git show --format=format:%B $LAST_SYNC_COMMIT | grep -Po '(?<=^Sync-with-trunk: r)[0-9]+$' | tail -n1) NEXT_SYNC_REV=$(sha1_to_rev $BRANCH_EXTRACT) [ "$LAST_SYNC_REV" = "$NEXT_SYNC_REV" ] && ( echo "No changes since last sync; aborting." exit 1 ) BRANCH_MERGE=sync/merge-r$NEXT_SYNC_REV echo "#### Step 2. Rebase '$BRANCH_EXTRACT' on the last sync commit" [ "$(git branch --list $BRANCH_MERGE)" ] || git branch $BRANCH_MERGE $BRANCH_EXTRACT [ ! -d tmp/sync-merge ] && git worktree add tmp/sync-merge $BRANCH_MERGE ( cd tmp/sync-merge git checkout $BRANCH_MERGE git reset -q --hard $BRANCH_EXTRACT git rebase --onto $LAST_SYNC_COMMIT $(rev_to_sha1 $BRANCH_EXTRACT $LAST_SYNC_REV) $BRANCH_MERGE ) echo "#### Step 3. Merge '$BRANCH_MERGE' into '$(git rev-parse --abbrev-ref HEAD)'" commit_message=$( commits=$(git log --oneline --format='%H %<(61,trunc)%s' $LAST_SYNC_COMMIT..$BRANCH_MERGE) echo "Merge changes from Ruby trunk r$LAST_SYNC_REV..r$NEXT_SYNC_REV" echo "" echo "* ruby-trunk r$LAST_SYNC_REV..r$NEXT_SYNC_REV: ($(echo "$commits" | wc -l) commits)" echo "$commits" | while read line; do sha1=$(echo "$line" | cut -f1 -d' ') cmsg=$(echo "$line" | cut -f2- -d' ') echo " (r$(sha1_to_rev $sha1)) $cmsg" done echo "" echo "Sync-with-trunk: r$NEXT_SYNC_REV" ) if git merge --no-ff --no-commit $BRANCH_MERGE; then git commit -m "$commit_message" else echo "Merge failed; fix conflict and commit with message:" echo "" echo "$commit_message" exit 1 fi openssl-2.0.9/tool/update-gh-pages000077500000000000000000000006251336157045000170720ustar00rootroot00000000000000#!/bin/sh set -e # Generates RDoc HTML and update gh-pages branch. HEAD_DESCRIPTION=$(git describe --dirty --always --abbrev=12) [ -d html ] && rm -r html rake rdoc [ ! -d tmp/gh-pages ] && git worktree add tmp/gh-pages gh-pages ( cd tmp/gh-pages git rm -r . cp -r ../../html/* . rm created.rid js/*.gz # to avoid unnecessary change :x git add . git commit -m "Sync with $HEAD_DESCRIPTION" )